diff --git a/.conf/development/back/Dockerfile b/.conf/development/back/Dockerfile new file mode 100644 index 0000000..7985126 --- /dev/null +++ b/.conf/development/back/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3.9-alpine + +ENV PYTHONUNBUFFERED=1 + +WORKDIR /back + +COPY ./.conf/development/back/run.sh /scripts/run.sh +ENV PATH="/scripts:$PATH" + +COPY ./back/requirements.txt . + +RUN apk add --update --no-cache postgresql-client zlib-dev jpeg-dev libwebp-dev && \ + apk add --update --no-cache --virtual .tmp-deps \ + build-base postgresql-dev musl-dev linux-headers && \ + pip install -r requirements.txt && \ + apk del .tmp-deps && \ + adduser --disabled-password --no-create-home back && \ + mkdir -p /vol/static && \ + mkdir -p /vol/media && \ + chmod -R +x /scripts + +COPY ./back . + +# TODO : Set user back, was removed for the collectstatic command to run succesfully +#USER back +CMD ["run.sh"] diff --git a/.conf/development/back/run.sh b/.conf/development/back/run.sh new file mode 100644 index 0000000..d54eae0 --- /dev/null +++ b/.conf/development/back/run.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +BACKEND_PORT=${BACKEND_PORT} +set -e + +python manage.py wait_for_db +python manage.py collectstatic --noinput +python manage.py migrate + +python manage.py runserver 0.0.0.0:$BACKEND_PORT \ No newline at end of file diff --git a/.conf/development/nginx/Dockerfile b/.conf/development/nginx/Dockerfile new file mode 100644 index 0000000..b212d84 --- /dev/null +++ b/.conf/development/nginx/Dockerfile @@ -0,0 +1,7 @@ +FROM nginx:1.22.0-alpine-perl + +ENV NGINX_ENVSUBST_TEMPLATE_SUFFIX=.tpl + +COPY ./.conf/development/nginx/default.conf.tpl /etc/nginx/templates/default.conf.tpl + +CMD ["nginx", "-g", "daemon off;"] diff --git a/.conf/development/nginx/default.conf.tpl b/.conf/development/nginx/default.conf.tpl new file mode 100644 index 0000000..0cbdda1 --- /dev/null +++ b/.conf/development/nginx/default.conf.tpl @@ -0,0 +1,21 @@ +server { + listen ${SERVER_HTTP_PORT}; + listen [::]:${SERVER_HTTP_PORT}; + + location /static { + alias /vol/static; + } + + location /media { + alias /vol/media; + } + + location / { + proxy_pass http://${BACKEND_NAME}:${BACKEND_PORT}; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + } +} \ No newline at end of file diff --git a/.conf/production/back/Dockerfile b/.conf/production/back/Dockerfile new file mode 100644 index 0000000..4c7db12 --- /dev/null +++ b/.conf/production/back/Dockerfile @@ -0,0 +1,31 @@ +FROM python:3.9-alpine + +ENV PYTHONUNBUFFERED=1 + +WORKDIR /back + +COPY ./.conf/production/back/run.sh /scripts/run.sh +ENV PATH="/scripts:$PATH" + +COPY ./back/requirements.txt . + +RUN apk add --update --no-cache postgresql-client zlib-dev jpeg-dev libwebp-dev && \ + apk add --update --no-cache --virtual .tmp-deps \ + build-base postgresql-dev musl-dev linux-headers && \ + pip install -r requirements.txt && \ + apk del .tmp-deps && \ + adduser --disabled-password --no-create-home back && \ + mkdir -p /vol/media && \ + mkdir -p /vol/static && \ + chmod -R +x /scripts + +COPY ./back . +COPY ./static /back/static + +RUN chown -R back:back /vol && \ + chmod -R 755 /vol && \ + chown -R back:back /back && \ + chmod -R 755 /back + +USER back +CMD ["run.sh"] diff --git a/.conf/production/back/run.sh b/.conf/production/back/run.sh new file mode 100644 index 0000000..68f56ac --- /dev/null +++ b/.conf/production/back/run.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +BACKEND_PORT=${BACKEND_PORT} +set -e + +python manage.py wait_for_db +python manage.py collectstatic --noinput +python manage.py migrate + +uwsgi --socket :"$BACKEND_PORT" --workers 4 --master --enable-threads --module back.wsgi \ No newline at end of file diff --git a/.conf/production/nginx/Dockerfile b/.conf/production/nginx/Dockerfile new file mode 100644 index 0000000..ba75aad --- /dev/null +++ b/.conf/production/nginx/Dockerfile @@ -0,0 +1,12 @@ +FROM nginx:1.22.0-alpine-perl + +ENV NGINX_ENVSUBST_TEMPLATE_SUFFIX=.tpl + +COPY ./.conf/production/nginx/default.conf.tpl /etc/nginx/templates/default.conf.tpl +COPY ./.conf/production/nginx/uwsgi_params /etc/nginx/uwsgi_params + +RUN mkdir -p /var/log/nginx/back && \ + touch /var/log/nginx/back/access.log && \ + touch /var/log/nginx/back/error.log + +CMD ["nginx", "-g", "daemon off;"] diff --git a/.conf/production/nginx/default.conf.tpl b/.conf/production/nginx/default.conf.tpl new file mode 100644 index 0000000..9df886f --- /dev/null +++ b/.conf/production/nginx/default.conf.tpl @@ -0,0 +1,21 @@ +server { + listen ${SERVER_PORT}; + server_name ${SERVER_HOST}; + + access_log /var/log/nginx/back/access.log; + error_log /var/log/nginx/back/error.log warn; + + location / { + uwsgi_pass ${BACKEND_PROXY}; + include /etc/nginx/uwsgi_params; + client_max_body_size 10M; + } + + location /static { + alias /vol/static; + } + + location /media { + alias /vol/media; + } +} diff --git a/.conf/production/nginx/uwsgi_params b/.conf/production/nginx/uwsgi_params new file mode 100644 index 0000000..458c257 --- /dev/null +++ b/.conf/production/nginx/uwsgi_params @@ -0,0 +1,13 @@ +uwsgi_param QUERY_STRING $query_string; +uwsgi_param REQUEST_METHOD $request_method; +uwsgi_param CONTENT_TYPE $content_type; +uwsgi_param CONTENT_LENGTH $content_length; +uwsgi_param REQUEST_URI $request_uri; +uwsgi_param PATH_INFO $document_uri; +uwsgi_param DOCUMENT_ROOT $document_root; +uwsgi_param SERVER_PROTOCOL $server_protocol; +uwsgi_param REMOTE_ADDR $remote_addr; +uwsgi_param REMOTE_PORT $remote_port; +uwsgi_param SERVER_ADDR $server_addr; +uwsgi_param SERVER_PORT $server_port; +uwsgi_param SERVER_NAME $server_name; \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3c3629e..9253017 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,14 @@ -node_modules +.idea +.DS_Store + +*.py[cod] +*$py.class + +back/tmp/ +node_modules/ +__pycache__/ + +static/dist/*.js +static/dist/*.css +.conf/development/conf.env +.conf/production/conf.env diff --git a/README.md b/README.md index 2e3d6a6..387209f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,22 @@ # BeerCrackerz -The website for beer lover, to share the best spot to crack a beer, or to easily refill this beverage of the gods! -Soon enough an alpha will be open for you to test this ! +[![License](https://img.shields.io/github/license/MesseBasseProduction/BeerCrackerz.svg)](https://github.com/MesseBasseProduction/BeerCrackerz/blob/master/LICENSE.md) +![](https://badgen.net/badge/version/0.1.0/blue) -https://github.com/pointhi/leaflet-color-markers +Welcome, fellow beer lovers. BeerCrackerz is a community web app to list the best spots to drink a fresh one while you're outside. It provides a well-known map interface so it is really easy to browse, find or add unique spots! -https://leafletjs.com/ +You want to try it ? We are currently running an instance just so you can try (and add your personnal best places) : -https://www.svgrepo.com/svg/287438/info +[https://beercrackerz.org](https://beercrackerz.org) -https://github.com/Leaflet/Leaflet.markercluster +## Get Started + +In order to install BeerCrackerz on your system and run your own instance, please refer the the [**Installation**](https://github.com/MesseBasseProduction/BeerCrackerz/wiki) wiki entry. + +## About + +BeerCrackerz is an open-source software edited and hosted by [Messe Basse Production](https://messe-basse-production.com/), developper by [Arthur Beaulieu](https://github.com/ArthurBeaulieu) and [Raphaël Beekmann](https://github.com/Asiberus) + +#### Technologies + +[OpenStreetMap](https://www.openstreetmap.org/), [ESRI](https://www.esri.com), [LeafletJs](https://leafletjs.com/), [Leaflet-MarkerCluster](https://github.com/Leaflet/Leaflet.markercluster), [Leaflet-Color-Markers](https://github.com/pointhi/leaflet-color-markers), [SVGRepo](https://www.svgrepo.com/) diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 3abf2d9..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,65 +0,0 @@ -# BeerCrackerz roadmap - -## Spot - -### Création de spot - -- [ ] tags (liste finie ; chill, view, animated, nature, city, parc, seeside, sea; port (a elargir au besoin)) -- [ ] photo (pas obligatoire) -- [x] note -- [x] description (faculattive) -- [ ] type de posage ; bench, chair, on the floor, camping chair required, toilet nearby, trash nearby -- [ ] availability : often use, often free - -### Affichage de spot - -- [ ] Pour spot, si user dans la zone autour, ajouter au popup du spot un vote sur la note du spot -- [ ] Si user dans zone spot, il peux incxrémenter un compteur de beer cracker dans le spot -- [ ] Upload photo de spots -- [ ] Afficher compteur de beercrackerz sur un spot direct dans la popup -- [ ] Spot espace commentaitre (accesible que depuis la zoine pour écrire, pas pour lire) -- [ ] Pour photos et coms, il fut cliquer sur un bouton du popup de spot qui ouvre modal de détail sur le spot -- [ ] afficher info sur popup (availability, tags etc) - -## Vendeur - -### Création de vendeur - -- [ ] tags (epicerie, maasin, etc) -- [ ] dispose de frigo -- [x] prix cher, moy ou pas - -### Affichage du Vendeur - -- [ ] affichage des tags et des prix - -## UI/UX - -### Commands - -- [x] hide/show des cercles dans cmd bar -- [ ] le plus proche de moi (fonction recherche dans modal, calc distance) -- [x] cmd profil (voir interface) : -- [x] clusters -- [ ] modal qui display profil user, + stats -- [ ] bières bues, spots ajoutés, vendeurs ajoutés, score -- [ ] accès au classement par score des beercrackerz -- [ ] mes contributions -- [ ] update pp, mail etc classique user syst - -### Scoring - -- [ ] chaque action suivante rapporte des points ; anciennetée, spot ajouté, vendeur ajouté, beer consommé, vote donné sur spot, commentaire etc -- [ ] scoreboard a faire pour classer par points les users - -### Interface - -- [x] ajouter layers de map satelite -- [x] fond de carte plus smooth, moins informatif -- [x] ajouter pp user en haut droite pour accéder profil -- [x] ajouter BeerCrackerz a coté pp coté gauche -- [ ] logo beercrackerz - - -En vrac : -offline/online status in debug or not ? diff --git a/assets/dist/BeerCrackerz.bundle.css b/assets/dist/BeerCrackerz.bundle.css deleted file mode 100644 index 1f51ff2..0000000 --- a/assets/dist/BeerCrackerz.bundle.css +++ /dev/null @@ -1 +0,0 @@ -@-webkit-keyframes flashing-logo{0%{-webkit-text-fill-color:transparent;background:linear-gradient(60deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:0}15%{-webkit-text-fill-color:transparent;background:linear-gradient(120deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:0}58%{-webkit-text-fill-color:transparent;background:-webkit-gradient(linear,left top,left bottom,from(#97ea9b),color-stop(80%,#ad7fe6));background:linear-gradient(180deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:1;-webkit-transform:scale(1.1);transform:scale(1.1)}to{-webkit-text-fill-color:transparent;background:linear-gradient(240deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:0;-webkit-transform:scale(1);transform:scale(1)}}@keyframes flashing-logo{0%{-webkit-text-fill-color:transparent;background:linear-gradient(60deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:0}15%{-webkit-text-fill-color:transparent;background:linear-gradient(120deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:0}58%{-webkit-text-fill-color:transparent;background:-webkit-gradient(linear,left top,left bottom,from(#97ea9b),color-stop(80%,#ad7fe6));background:linear-gradient(180deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:1;-webkit-transform:scale(1.1);transform:scale(1.1)}to{-webkit-text-fill-color:transparent;background:linear-gradient(240deg,#97ea9b,#ad7fe6 80%);-webkit-background-clip:text;background-clip:text;opacity:0;-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes drop-nav-link{0%{margin-bottom:20rem;-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}to{margin-bottom:0;-webkit-transform:rotate(0);transform:rotate(0)}}@keyframes drop-nav-link{0%{margin-bottom:20rem;-webkit-transform:rotate(-12deg);transform:rotate(-12deg)}to{margin-bottom:0;-webkit-transform:rotate(0);transform:rotate(0)}}@-webkit-keyframes beating{0%{-webkit-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.01);transform:scale(1.01)}20%{-webkit-transform:scale(1);transform:scale(1)}80%{-webkit-transform:scale(1);transform:scale(1)}90%{-webkit-transform:scale(1.02);transform:scale(1.02)}to{-webkit-transform:scale(1);transform:scale(1)}}@keyframes beating{0%{-webkit-transform:scale(1);transform:scale(1)}10%{-webkit-transform:scale(1.01);transform:scale(1.01)}20%{-webkit-transform:scale(1);transform:scale(1)}80%{-webkit-transform:scale(1);transform:scale(1)}90%{-webkit-transform:scale(1.02);transform:scale(1.02)}to{-webkit-transform:scale(1);transform:scale(1)}}*{-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;padding:0}body,html{font-size:62.5%;height:100%;overflow:hidden;width:100%}body{background:#181818;color:#d4d4d4;font-family:sans-serif;position:relative}h1{color:#181818;font-size:2.8rem;margin-bottom:1.2rem}h2{font-size:2.4rem}a{color:#a1ff86}a,label,p{font-size:1.2rem;margin-bottom:1.2rem}label,p{color:#2e2e2e}label{font-style:italic}input,textarea{border:1px solid #424242;border-radius:.5rem;display:block;margin:.5rem auto 1.2rem;padding:.5rem;-webkit-transition:border .2s;transition:border .2s;width:100%}input.error{border-color:#ff5454}button{background-color:hsla(0,0%,91%,.667);border:1px solid #424242;border-radius:.5rem;cursor:pointer;display:block;margin:.5rem auto;padding:.5rem;-webkit-transition:background-color .2s;transition:background-color .2s;width:100%}button:active,button:focus,button:hover{background-color:hsla(0,0%,85%,.667)}button.validate{background-color:rgba(161,255,134,.667)}button.cancel{background-color:hsla(0,100%,80%,.667)}main.beer-crakerz-map{height:100%;width:100%;z-index:10}main.beer-crakerz-map .new-poi{text-align:center}main.beer-crakerz-map .new-poi p{font-size:1.6rem;font-weight:700}main.beer-crakerz-map .new-poi button{margin:1.2rem 0}main.beer-crakerz-map .marker-tooltip{font-style:italic;text-align:center}nav{-webkit-box-orient:vertical;-webkit-box-direction:normal;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;position:absolute;right:1rem;top:1rem;width:4.8rem;z-index:20}nav img{border-radius:50%;-webkit-box-shadow:0 0 10px #424242;box-shadow:0 0 10px #424242;cursor:pointer;width:100%}nav .cmd-bar .cmd-wrapper{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;background:#fff;background-clip:padding-box;border:2px solid rgba(0,0,0,.2);border-radius:.5rem;display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:center;margin:1rem 0;padding:.2rem}nav .cmd-bar .cmd-wrapper:first-child{margin-top:0}nav .cmd-bar .cmd-wrapper img{-webkit-box-shadow:inherit;box-shadow:inherit;-webkit-transition:all .2s;transition:all .2s}nav .cmd-bar .cmd-wrapper img:active,nav .cmd-bar .cmd-wrapper img:focus,nav .cmd-bar .cmd-wrapper img:hover{border:1px solid #b9b9b9;border-radius:.5rem;-webkit-filter:invert(70%) sepia(67%) saturate(455%) hue-rotate(67deg) brightness(163%) contrast(85%);filter:invert(70%) sepia(67%) saturate(455%) hue-rotate(67deg) brightness(163%) contrast(85%);padding:.2rem}nav .cmd-bar .cmd-wrapper img[class$=-on]{-webkit-filter:invert(53%) sepia(30%) saturate(1977%) hue-rotate(155deg) brightness(88%) contrast(102%);filter:invert(53%) sepia(30%) saturate(1977%) hue-rotate(155deg) brightness(88%) contrast(102%)}nav .cmd-bar .cmd-wrapper.logo img{padding:.1rem}nav .cmd-bar .cmd-wrapper.logo img:active,nav .cmd-bar .cmd-wrapper.logo img:focus,nav .cmd-bar .cmd-wrapper.logo img:hover{-webkit-filter:inherit;filter:inherit}.notification-wrapper{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;background-color:hsla(0,0%,100%,.8);border-radius:.5rem;-webkit-box-shadow:0 0 10px #424242;box-shadow:0 0 10px #424242;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;height:3rem;justify-content:center;left:calc(50% - 15rem);opacity:0;position:absolute;top:-4rem;-webkit-transition:top .3s,opacity .2s ease-out;transition:top .3s,opacity .2s ease-out;width:30rem;z-index:40}.notification-wrapper.opened{opacity:1;top:1rem}.notification-wrapper .notification-message{font-style:italic;margin:0}.zoom-slider{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-orient:vertical;-webkit-box-direction:normal;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;background:#fff;background-clip:padding-box;border:2px solid rgba(0,0,0,.2);border-radius:.5rem;-webkit-box-shadow:0 0 10px #424242;box-shadow:0 0 10px #424242;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;height:20rem;justify-content:center;opacity:0;position:absolute;right:-6rem;top:calc(50% - 10rem);-webkit-transition:right .3s,opacity .2s ease-out;transition:right .3s,opacity .2s ease-out;width:4.8rem;z-index:20}.zoom-slider.opened{opacity:1;right:1rem}.zoom-slider p{cursor:pointer;font-size:1.6rem;font-weight:700;margin:.5rem 0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.zoom-slider p:last-child{margin-bottom:0}.zoom-slider .slider-wrapper{border-radius:.5rem;-webkit-box-shadow:0 0 10px #424242;box-shadow:0 0 10px #424242;height:80%;overflow:hidden;position:relative;width:20%}.zoom-slider .slider-wrapper .slider-position{background-color:lime;bottom:0;position:absolute;-webkit-transition:height .2s;transition:height .2s;width:100%}.overlay{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;background:hsla(0,0%,100%,.8);display:none;height:100%;justify-content:center;opacity:0;position:absolute;top:0;-webkit-transition:opacity .3s;transition:opacity .3s;width:100%;z-index:30}.overlay [class$=-modal]{background:hsla(0,0%,100%,.8);border-radius:.5rem;-webkit-box-shadow:0 0 10px #424242;box-shadow:0 0 10px #424242;padding:3rem 4rem;position:relative;text-align:center}.overlay [class$=-modal] h1{margin-bottom:3rem}.overlay [class$=-modal] button{margin-top:3rem}.overlay [class$=-modal] .modal-close{color:#999;cursor:pointer;font-size:2.2rem;font-weight:700;line-height:1.4rem;position:absolute;right:1rem;top:1rem}.overlay [class$=-modal] .button-wrapper{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:center}.overlay [class$=-modal] .button-wrapper button{margin-left:1rem;margin-right:1rem}.overlay .edit-mark-modal,.overlay .new-mark-modal{width:calc(100% - 2rem)}.overlay .edit-mark-modal h1,.overlay .edit-mark-modal p,.overlay .new-mark-modal h1,.overlay .new-mark-modal p{text-align:center}.overlay .edit-mark-modal .rating,.overlay .new-mark-modal .rating{margin-bottom:1.2rem}.overlay .edit-mark-modal .rating img,.overlay .new-mark-modal .rating img{cursor:pointer;height:2.5rem;margin:0 .25rem;width:2.5rem}.overlay .edit-mark-modal .rating img.active,.overlay .new-mark-modal .rating img.active{-webkit-filter:invert(95%) sepia(39%) saturate(3136%) hue-rotate(323deg) brightness(96%) contrast(115%);filter:invert(95%) sepia(39%) saturate(3136%) hue-rotate(323deg) brightness(96%) contrast(115%)}.overlay .edit-mark-modal .rating img.active.selected,.overlay .new-mark-modal .rating img.active.selected{-webkit-filter:invert(75%) sepia(9%) saturate(4002%) hue-rotate(67deg) brightness(81%) contrast(83%);filter:invert(75%) sepia(9%) saturate(4002%) hue-rotate(67deg) brightness(81%) contrast(83%)}.overlay .hide-show-modal .item{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:justify;-ms-flex-pack:justify;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-between;margin:1rem auto;max-width:15rem}.overlay .hide-show-modal .item img{height:2rem;margin-right:2rem}.overlay .hide-show-modal .item label{margin-bottom:0;margin-right:2rem}.overlay .hide-show-modal .item input{margin:0;width:auto}.overlay .user-profile-modal{height:calc(100% - 2rem);width:calc(100% - 2rem)}.overlay .user-profile-modal .item{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:justify;-ms-flex-pack:justify;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-between;margin:1rem auto;max-width:25rem}.overlay .user-profile-modal .item img{height:2rem;margin-right:2rem}.overlay .user-profile-modal .item label{margin-bottom:0;margin-right:2rem}.overlay .user-profile-modal .item input{margin:0;width:auto}.debug-container{background-color:hsla(0,0%,100%,.8);border-radius:.5rem;color:#000;left:1rem;padding:1rem;position:absolute;top:1rem;z-index:20}.debug-container p{margin-bottom:.2rem}.popup h1,.popup h2,.popup p{margin:0;text-align:center}.popup h1{font-size:2rem;margin-bottom:1.2rem}.popup .rating{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:center;-ms-flex-pack:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:center;width:100%}.popup .rating img{height:1.2rem}.popup .rating img.active{-webkit-filter:invert(95%) sepia(39%) saturate(3136%) hue-rotate(323deg) brightness(96%) contrast(115%);filter:invert(95%) sepia(39%) saturate(3136%) hue-rotate(323deg) brightness(96%) contrast(115%)}.popup .rating p{font-style:inherit;margin-left:.5rem}.popup p{font-style:italic}.popup h2{font-size:1.3rem;font-weight:inherit;margin-top:1.2rem}.popup footer{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-pack:justify;-ms-flex-pack:justify;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;justify-content:space-between;margin-top:1.2rem;position:relative}.popup footer img{cursor:pointer;height:2rem;margin-left:.5rem}.cluster-icon-wrapper{position:relative}.cluster-icon-wrapper .cluster-icon{-webkit-filter:drop-shadow(0 0 .25rem #424242);filter:drop-shadow(0 0 .25rem #424242);height:50px;line-height:50px;margin-left:-21px;margin-top:-13px;-webkit-transition:-webkit-transform .2s,-webkit-filter .2s;transition:-webkit-transform .2s,-webkit-filter .2s;transition:transform .2s,filter .2s;transition:transform .2s,filter .2s,-webkit-transform .2s,-webkit-filter .2s;width:auto}.cluster-icon-wrapper .cluster-icon:active,.cluster-icon-wrapper .cluster-icon:focus,.cluster-icon-wrapper .cluster-icon:hover{-webkit-filter:drop-shadow(0 0 .33rem #a8a8a8);filter:drop-shadow(0 0 .33rem #A8A8A8);-webkit-transform:scale(1.066);transform:scale(1.066)}.cluster-icon-wrapper .cluster-label{background-color:#fff;border:1px solid #000;border-radius:.75rem;-webkit-box-shadow:0 0 10px #424242;box-shadow:0 0 10px #424242;color:#000;font-size:1.1rem;font-weight:700;left:15px;padding:.1rem .4rem;position:absolute;top:-15px}.leaflet-control-layers.leaflet-control{-webkit-transition:all .2s;transition:all .2s}.leaflet-control-layers.leaflet-control a{margin-bottom:0}.leaflet-control-layers-expanded{padding:2rem!important}.leaflet-control-layers-expanded label{margin:0}.leaflet-control-layers-expanded label div{-webkit-box-align:center;-ms-flex-align:center;-webkit-box-orient:horizontal;-webkit-box-direction:reverse;-webkit-box-pack:justify;-ms-flex-pack:justify;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-direction:row-reverse;flex-direction:row-reverse;justify-content:space-between}.leaflet-control-layers-expanded label div input{width:auto}.leaflet-control-layers-expanded label div input,.leaflet-control-layers-expanded label div p{margin:0}.leaflet-control-layers-expanded label div p{margin-right:1rem}.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive{-webkit-filter:drop-shadow(0 0 .25rem #424242);filter:drop-shadow(0 0 .25rem #424242);-webkit-transition:-webkit-transform .2s,-webkit-filter .2s;transition:-webkit-transform .2s,-webkit-filter .2s;transition:transform .2s,filter .2s;transition:transform .2s,filter .2s,-webkit-transform .2s,-webkit-filter .2s}.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive:active,.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive:focus,.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive:hover{-webkit-filter:drop-shadow(0 0 .33rem #a8a8a8);filter:drop-shadow(0 0 .33rem #A8A8A8)}@media (min-width:720px){.overlay [class$=-modal]{max-width:50%}} diff --git a/assets/dist/BeerCrackerz.bundle.js b/assets/dist/BeerCrackerz.bundle.js deleted file mode 100644 index 3618cfd..0000000 --- a/assets/dist/BeerCrackerz.bundle.js +++ /dev/null @@ -1 +0,0 @@ -!function(){"use strict";var e={d:function(t,r){for(var n in r)e.o(r,n)&&!e.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:r[n]})},o:function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}},t={};e.d(t,{default:function(){return w}});var r=Object.freeze({blue:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-blue.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),gold:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-gold.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),red:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-red.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),green:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-green.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),orange:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-orange.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),yellow:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-yellow.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),violet:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-violet.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),grey:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-grey.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),black:new window.L.Icon({iconUrl:"assets/img/marker/marker-icon-black.png",shadowUrl:"assets/img/marker/marker-shadow.png",iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]}),user:new window.L.Icon({iconUrl:"assets/img/marker/user-position.png",shadowUrl:"assets/img/marker/user-position-shadow.png",iconSize:[32,32],iconAnchor:[16,16],popupAnchor:[1,-34],shadowSize:[32,32]})});function n(e,t){for(var r=0;r : -"),n.innerHTML="".concat(e("lng")," : -"),a.innerHTML="".concat(e("updates")," : 0"),o.innerHTML="".concat(e("accuracy")," : -"),i.innerHTML="".concat(e("highAccuracy")," : -"),c.innerHTML="".concat(e("posAge")," : -"),s.innerHTML="".concat(e("posTimeout")," : -"),l.innerHTML="".concat(e("zoom")," : -"),u.innerHTML="".concat(e("marks")," : -"),d.innerHTML=e("export"),t.appendChild(r),t.appendChild(n),t.appendChild(a),t.appendChild(o),t.appendChild(i),t.appendChild(c),t.appendChild(s),t.appendChild(l),t.appendChild(u),t.appendChild(d),d.addEventListener("click",window.BeerCrackerz.downloadData.bind(window.BeerCrackerz)),t}},{key:"updateDebugInterface",value:function(t,r,n){if(!0===window.DEBUG){var a=window.BeerCrackerz,o=a.nls.debug.bind(a.nls),i=parseInt(t.querySelector(".debug-updates-amount").innerHTML.split(" : ")[1])+1,c=a.marks.spot.length+a.marks.store.length+a.marks.bar.length;t.querySelector(".debug-user-lat").innerHTML="\n ".concat(o("lat")," : ").concat(r.lat,"\n "),t.querySelector(".debug-user-lng").innerHTML="\n ".concat(o("lng")," : ").concat(r.lng,"\n "),t.querySelector(".debug-updates-amount").innerHTML="\n ".concat(o("updates")," : ").concat(i,"\n "),t.querySelector(".debug-user-accuracy").innerHTML="\n ".concat(o("accuracy")," : ").concat(e.precisionRound(r.accuracy,2),"m\n "),t.querySelector(".debug-high-accuracy").innerHTML="\n ".concat(o("highAccuracy")," : ").concat(!0===n.enableHighAccuracy?o("enabled"):o("disabled"),"\n "),t.querySelector(".debug-pos-max-age").innerHTML="\n ".concat(o("posAge")," : ").concat(n.maximumAge/1e3,"s\n "),t.querySelector(".debug-pos-timeout").innerHTML="\n ".concat(o("posTimeout")," : ").concat(n.timeout/1e3,"s\n "),t.querySelector(".debug-zoom-level").innerHTML="\n ".concat(o("zoom")," : ").concat(a.map.getZoom(),"\n "),t.querySelector(".debug-marks-amount").innerHTML="\n ".concat(o("marks")," : ").concat(c,"\n ")}}},{key:"getPreference",value:function(e){return localStorage.getItem(e)||null}},{key:"setPreference",value:function(e,t){localStorage.setItem(e,t)}},{key:"RANGE_COLOR",get:function(){return"#ffd87d"}},{key:"USER_COLOR",get:function(){return"#63fff5"}},{key:"SPOT_COLOR",get:function(){return"#26ad23"}},{key:"STORE_COLOR",get:function(){return"#247dc9"}},{key:"BAR_COLOR",get:function(){return"#ca2a3d"}},{key:"CIRCLE_RADIUS",get:function(){return 100}},{key:"NEW_MARKER_RANGE",get:function(){return 200}},{key:"MAP_BOUNDS",get:function(){return window.L.latLngBounds(window.L.latLng(-89.98155760646617,-180),window.L.latLng(89.99346179538875,180))}},{key:"HIGH_ACCURACY",get:function(){return{enableHighAccuracy:!0,maximumAge:1e3,timeout:900}}},{key:"OPTIMIZED_ACCURACY",get:function(){return{enableHighAccuracy:!1,maximumAge:3e4,timeout:29e3}}},{key:"SUPPORTED_LANGUAGE",get:function(){return["en","fr","es","de"]}}],null&&o(t.prototype,null),r&&o(t,r),Object.defineProperty(t,"prototype",{writable:!1}),e}();function c(e,t){for(var r=0;ri.CIRCLE_RADIUS&&console.log("Too far"),o!==t.user.username?a.removeChild(a.querySelector("#popup-edit")):(a.querySelector("#edit-mark").addEventListener("click",t.editMarker.bind(t,e),!1),a.querySelector("#delete-mark").addEventListener("click",t.deleteMarker.bind(t,e),!1)),e.color=i["".concat(e.type.toUpperCase(),"_COLOR")],e.circle=t.drawCircle(e),e.tooltip=window.L.tooltip({permanent:!0,direction:"center",className:"marker-tooltip",interactive:!0}).setContent(e.name).setLatLng(e.circle.getLatLng()),"true"===i.getPreference("poi-marker-label")&&e.tooltip.addTo(t.map),r(a)}))}))}},{key:"drawCircle",value:function(e){return window.L.circle(e,{color:e.color,fillColor:e.color,opacity:0,fillOpacity:0,radius:e.radius?e.radius:i.CIRCLE_RADIUS}).addTo(this.map)}},{key:"setMarkerCircles",value:function(e,t){for(var r=0;rOpenStreetMap',maxZoom:21,maxNativeZoom:19,minZoom:2}),satEsri:window.L.tileLayer("https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",{attribution:'© Esri Imagery',minZoom:2,maxNativeZoom:19,maxZoom:21})});function u(e,t){for(var r=0;r".concat(e.nls.map("planLayerOSM"),"

")]=r,a["

".concat(e.nls.map("satLayerEsri"),"

")]=n,i.getPreference("map-plan-layer"))switch(i.getPreference("map-plan-layer")){case e.nls.map("planLayerOSM"):r.addTo(e._map);break;case e.nls.map("satLayerEsri"):n.addTo(e._map);break;default:r.addTo(e._map)}else r.addTo(e._map);window.L.control.layers(a,{},{position:"bottomright"}).addTo(e._map),e._zoomSlider=new d(e._map),t()}))}},{key:"_initEvents",value:function(){var e=this;return new Promise((function(t){document.getElementById("user-profile").addEventListener("click",e.userProfileModal.bind(e)),document.getElementById("hide-show").addEventListener("click",e.hidShowModal.bind(e)),document.getElementById("center-on").addEventListener("click",e.toggleFocusLock.bind(e)),document.getElementById("overlay").addEventListener("click",e.closeModal.bind(e)),e._map.on("click",e.mapClicked.bind(e)),e._map.on("drag",(function(){e._map.panInsideBounds(i.MAP_BOUNDS,{animate:!0}),"true"===i.getPreference("map-center-on-user")&&e.toggleFocusLock()})),e._map.on("zoomstart",(function(){e._isZooming=!0,"true"===i.getPreference("poi-show-circle")&&(e.setMarkerCircles(e._marks.spot,!1),e.setMarkerCircles(e._marks.store,!1),e.setMarkerCircles(e._marks.bar,!1),e.setMarkerCircles([e._user],!1),e.setMarkerCircles([{circle:e._user.range}],!1))})),e._map.on("zoomend",(function(){e._isZooming=!1,"true"===i.getPreference("poi-show-circle")&&e._map.getZoom()>=15&&(e.setMarkerCircles(e._marks.spot,!0),e.setMarkerCircles(e._marks.store,!0),e.setMarkerCircles(e._marks.bar,!0),e.setMarkerCircles([e._user],!0),e.setMarkerCircles([{circle:e._user.range}],!0)),"true"===i.getPreference("poi-marker-label")&&(e._map.getZoom()<15?(e.setMarkerLabels(e._marks.spot,!1),e.setMarkerLabels(e._marks.store,!1),e.setMarkerLabels(e._marks.bar,!1)):(e.setMarkerLabels(e._marks.spot,!0),e.setMarkerLabels(e._marks.store,!0),e.setMarkerLabels(e._marks.bar,!0))),e.updateDebugUI()})),e._map.on("baselayerchange",(function(e){i.setPreference("map-plan-layer",i.stripDom(e.name))})),t()}))}},{key:"_initMarkers",value:function(){var e=this;return new Promise((function(t){var r={animateAddingMarkers:!0,disableClusteringAtZoom:18,spiderfyOnMaxZoom:!1};e._clusters.spot=new window.L.MarkerClusterGroup(Object.assign(r,{iconCreateFunction:function(e){return window.L.divIcon({className:"cluster-icon-wrapper",html:'\n \n '.concat(e.getChildCount(),"\n ")})}})),e._clusters.store=new window.L.MarkerClusterGroup(Object.assign(r,{iconCreateFunction:function(e){return window.L.divIcon({className:"cluster-icon-wrapper",html:'\n \n '.concat(e.getChildCount(),"\n ")})}})),e._clusters.bar=new window.L.MarkerClusterGroup(Object.assign(r,{iconCreateFunction:function(e){return window.L.divIcon({className:"cluster-icon-wrapper",html:'\n \n '.concat(e.getChildCount(),"\n ")})}})),"true"===i.getPreference("poi-show-spot")&&e._map.addLayer(e._clusters.spot),"true"===i.getPreference("poi-show-store")&&e._map.addLayer(e._clusters.store),"true"===i.getPreference("poi-show-bar")&&e._map.addLayer(e._clusters.bar);for(var n=function(t){e.markPopupFactory(t).then((function(r){t.dom=r,t.marker=e.placeMarker(t),e._marks[t.type].push(t),e._clusters[t.type].addLayer(t.marker)}))},a=JSON.parse(i.getPreference("saved-spot"))||[],o=0;o=i.CIRCLE_RADIUS&&t[r].circle.visible&&(t[r].circle.visible=!1,t[r].circle.setStyle({opacity:0,fillOpacity:0}))}};"true"===i.getPreference("poi-show-circle")&&(t(this._marks.spot),t(this._marks.store),t(this._marks.bar),t([this._user]))}},{key:"formatSavedMarker",value:function(e){return{type:e.type,lat:e.lat,lng:e.lng,name:e.name,description:e.description,user:e.username||this.user.username,userId:e.userId||this.user.id,dom:null,rate:e.rate,marker:null,circle:null}}},{key:"editMarker",value:function(e){this._map.closePopup(),this.editMarkModal(e)}},{key:"deleteMarker",value:function(e){var t=this;this.deleteMarkModal((function(r){if(!0===r){for(var n=t._marks[e.type],a=0;a : -"),r.innerHTML="".concat(e("lng")," : -"),a.innerHTML="".concat(e("updates")," : 0"),i.innerHTML="".concat(e("accuracy")," : -"),o.innerHTML="".concat(e("highAccuracy")," : -"),c.innerHTML="".concat(e("posAge")," : -"),s.innerHTML="".concat(e("posTimeout")," : -"),u.innerHTML="".concat(e("zoom")," : -"),l.innerHTML="".concat(e("marks")," : -"),d.innerHTML=e("export"),n.appendChild(t),n.appendChild(r),n.appendChild(a),n.appendChild(i),n.appendChild(o),n.appendChild(c),n.appendChild(s),n.appendChild(u),n.appendChild(l),n.appendChild(d),d.addEventListener("click",window.BeerCrackerz.downloadData.bind(window.BeerCrackerz)),n}},{key:"updateDebugInterface",value:function(n,t,r){if(!0===window.DEBUG){var a=window.BeerCrackerz,i=a.nls.debug.bind(a.nls),o=parseInt(n.querySelector(".debug-updates-amount").innerHTML.split(" : ")[1])+1,c=a.marks.spot.length+a.marks.store.length+a.marks.bar.length;n.querySelector(".debug-user-lat").innerHTML="\n ".concat(i("lat")," : ").concat(t.lat,"\n "),n.querySelector(".debug-user-lng").innerHTML="\n ".concat(i("lng")," : ").concat(t.lng,"\n "),n.querySelector(".debug-updates-amount").innerHTML="\n ".concat(i("updates")," : ").concat(o,"\n "),n.querySelector(".debug-user-accuracy").innerHTML="\n ".concat(i("accuracy")," : ").concat(e.precisionRound(t.accuracy,2),"m\n "),n.querySelector(".debug-high-accuracy").innerHTML="\n ".concat(i("highAccuracy")," : ").concat(!0===r.enableHighAccuracy?i("enabled"):i("disabled"),"\n "),n.querySelector(".debug-pos-max-age").innerHTML="\n ".concat(i("posAge")," : ").concat(r.maximumAge/1e3,"s\n "),n.querySelector(".debug-pos-timeout").innerHTML="\n ".concat(i("posTimeout")," : ").concat(r.timeout/1e3,"s\n "),n.querySelector(".debug-zoom-level").innerHTML="\n ".concat(i("zoom")," : ").concat(a.map.getZoom(),"\n "),n.querySelector(".debug-marks-amount").innerHTML="\n ".concat(i("marks")," : ").concat(c,"\n ")}}},{key:"getPreference",value:function(e){return localStorage.getItem(e)||null}},{key:"setPreference",value:function(e,n){localStorage.setItem(e,n)}},{key:"RANGE_COLOR",get:function(){return"#ffd87d"}},{key:"USER_COLOR",get:function(){return"#63fff5"}},{key:"SPOT_COLOR",get:function(){return"#26ad23"}},{key:"STORE_COLOR",get:function(){return"#247dc9"}},{key:"BAR_COLOR",get:function(){return"#ca2a3d"}},{key:"CIRCLE_RADIUS",get:function(){return 100}},{key:"NEW_MARKER_RANGE",get:function(){return 200}},{key:"MAP_BOUNDS",get:function(){return window.L.latLngBounds(window.L.latLng(-89.98155760646617,-180),window.L.latLng(89.99346179538875,180))}},{key:"HIGH_ACCURACY",get:function(){return{enableHighAccuracy:!0,maximumAge:1e3,timeout:900}}},{key:"OPTIMIZED_ACCURACY",get:function(){return{enableHighAccuracy:!1,maximumAge:3e4,timeout:29e3}}},{key:"SUPPORTED_LANGUAGE",get:function(){return["en","fr","es","de"]}}],null&&t(n.prototype,null),r&&t(n,r),Object.defineProperty(n,"prototype",{writable:!1}),e}();function a(e,n){for(var t=0;t - -

{{MODAL_TITLE}}

-

{{MODAL_DESC}}

-
- - -
- diff --git a/assets/html/modal/editbar.html b/assets/html/modal/editbar.html deleted file mode 100644 index 567b294..0000000 --- a/assets/html/modal/editbar.html +++ /dev/null @@ -1,20 +0,0 @@ -
- -

{{MODAL_TITLE}}

- - - - - -
- rating-star - rating-star - rating-star - rating-star - rating-star -
-
- - -
-
diff --git a/assets/html/modal/editspot.html b/assets/html/modal/editspot.html deleted file mode 100644 index b3430a8..0000000 --- a/assets/html/modal/editspot.html +++ /dev/null @@ -1,20 +0,0 @@ -
- -

{{MODAL_TITLE}}

- - - - - -
- rating-star - rating-star - rating-star - rating-star - rating-star -
-
- - -
-
diff --git a/assets/html/modal/editstore.html b/assets/html/modal/editstore.html deleted file mode 100644 index a734711..0000000 --- a/assets/html/modal/editstore.html +++ /dev/null @@ -1,20 +0,0 @@ -
- -

{{MODAL_TITLE}}

- - - - - -
- rating-star - rating-star - rating-star - rating-star - rating-star -
-
- - -
-
diff --git a/assets/html/modal/hideshow.html b/assets/html/modal/hideshow.html deleted file mode 100644 index ac4adaa..0000000 --- a/assets/html/modal/hideshow.html +++ /dev/null @@ -1,30 +0,0 @@ -
- -

{{MODAL_TITLE}}

-
- label-toggle - - -
-
- circle-toggle - - -
-
- show-spots - - -
-
- show-stores - - -
-
- show-bars - - -
- -
diff --git a/assets/html/modal/newbar.html b/assets/html/modal/newbar.html deleted file mode 100644 index 19b9f64..0000000 --- a/assets/html/modal/newbar.html +++ /dev/null @@ -1,19 +0,0 @@ -
- -

{{BAR_TITLE}}

-

{{BAR_SUBTITLE}}

- - - - - -
- rating-star - rating-star - rating-star -
-
- - -
-
diff --git a/assets/html/modal/newspot.html b/assets/html/modal/newspot.html deleted file mode 100644 index bd47f37..0000000 --- a/assets/html/modal/newspot.html +++ /dev/null @@ -1,21 +0,0 @@ -
- -

{{SPOT_TITLE}}

-

{{SPOT_SUBTITLE}}

- - - - - -
- rating-star - rating-star - rating-star - rating-star - rating-star -
-
- - -
-
diff --git a/assets/html/modal/newstore.html b/assets/html/modal/newstore.html deleted file mode 100644 index df6d5d1..0000000 --- a/assets/html/modal/newstore.html +++ /dev/null @@ -1,19 +0,0 @@ -
- -

{{STORE_TITLE}}

-

{{STORE_SUBTITLE}}

- - - - - -
- rating-star - rating-star - rating-star -
-
- - -
-
diff --git a/assets/html/modal/user.html b/assets/html/modal/user.html deleted file mode 100644 index 5e08a14..0000000 --- a/assets/html/modal/user.html +++ /dev/null @@ -1,15 +0,0 @@ - diff --git a/assets/html/popup/bar.html b/assets/html/popup/bar.html deleted file mode 100644 index 247c625..0000000 --- a/assets/html/popup/bar.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/assets/html/popup/spot.html b/assets/html/popup/spot.html deleted file mode 100644 index a8bc30d..0000000 --- a/assets/html/popup/spot.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/assets/html/popup/store.html b/assets/html/popup/store.html deleted file mode 100644 index 25791ce..0000000 --- a/assets/html/popup/store.html +++ /dev/null @@ -1,23 +0,0 @@ - diff --git a/assets/img/favicon/android-icon-144x144.png b/assets/img/favicon/android-icon-144x144.png deleted file mode 100644 index b4d43bb..0000000 Binary files a/assets/img/favicon/android-icon-144x144.png and /dev/null differ diff --git a/assets/img/favicon/android-icon-192x192.png b/assets/img/favicon/android-icon-192x192.png deleted file mode 100644 index 197cde4..0000000 Binary files a/assets/img/favicon/android-icon-192x192.png and /dev/null differ diff --git a/assets/img/favicon/android-icon-36x36.png b/assets/img/favicon/android-icon-36x36.png deleted file mode 100644 index d7b642a..0000000 Binary files a/assets/img/favicon/android-icon-36x36.png and /dev/null differ diff --git a/assets/img/favicon/android-icon-48x48.png b/assets/img/favicon/android-icon-48x48.png deleted file mode 100644 index f6bb35d..0000000 Binary files a/assets/img/favicon/android-icon-48x48.png and /dev/null differ diff --git a/assets/img/favicon/android-icon-72x72.png b/assets/img/favicon/android-icon-72x72.png deleted file mode 100644 index b0938fd..0000000 Binary files a/assets/img/favicon/android-icon-72x72.png and /dev/null differ diff --git a/assets/img/favicon/android-icon-96x96.png b/assets/img/favicon/android-icon-96x96.png deleted file mode 100644 index 4b8a353..0000000 Binary files a/assets/img/favicon/android-icon-96x96.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-114x114.png b/assets/img/favicon/apple-icon-114x114.png deleted file mode 100644 index d1a1194..0000000 Binary files a/assets/img/favicon/apple-icon-114x114.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-120x120.png b/assets/img/favicon/apple-icon-120x120.png deleted file mode 100644 index ad5eba2..0000000 Binary files a/assets/img/favicon/apple-icon-120x120.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-144x144.png b/assets/img/favicon/apple-icon-144x144.png deleted file mode 100644 index b4d43bb..0000000 Binary files a/assets/img/favicon/apple-icon-144x144.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-152x152.png b/assets/img/favicon/apple-icon-152x152.png deleted file mode 100644 index b61a663..0000000 Binary files a/assets/img/favicon/apple-icon-152x152.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-180x180.png b/assets/img/favicon/apple-icon-180x180.png deleted file mode 100644 index e2c73e8..0000000 Binary files a/assets/img/favicon/apple-icon-180x180.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-57x57.png b/assets/img/favicon/apple-icon-57x57.png deleted file mode 100644 index a64c182..0000000 Binary files a/assets/img/favicon/apple-icon-57x57.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-60x60.png b/assets/img/favicon/apple-icon-60x60.png deleted file mode 100644 index dacf326..0000000 Binary files a/assets/img/favicon/apple-icon-60x60.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-72x72.png b/assets/img/favicon/apple-icon-72x72.png deleted file mode 100644 index b0938fd..0000000 Binary files a/assets/img/favicon/apple-icon-72x72.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-76x76.png b/assets/img/favicon/apple-icon-76x76.png deleted file mode 100644 index 2170c4c..0000000 Binary files a/assets/img/favicon/apple-icon-76x76.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon-precomposed.png b/assets/img/favicon/apple-icon-precomposed.png deleted file mode 100644 index a4809cf..0000000 Binary files a/assets/img/favicon/apple-icon-precomposed.png and /dev/null differ diff --git a/assets/img/favicon/apple-icon.png b/assets/img/favicon/apple-icon.png deleted file mode 100644 index a4809cf..0000000 Binary files a/assets/img/favicon/apple-icon.png and /dev/null differ diff --git a/assets/img/favicon/favicon-16x16.png b/assets/img/favicon/favicon-16x16.png deleted file mode 100644 index 1a58469..0000000 Binary files a/assets/img/favicon/favicon-16x16.png and /dev/null differ diff --git a/assets/img/favicon/favicon-32x32.png b/assets/img/favicon/favicon-32x32.png deleted file mode 100644 index cc8b788..0000000 Binary files a/assets/img/favicon/favicon-32x32.png and /dev/null differ diff --git a/assets/img/favicon/favicon-96x96.png b/assets/img/favicon/favicon-96x96.png deleted file mode 100644 index fc2267c..0000000 Binary files a/assets/img/favicon/favicon-96x96.png and /dev/null differ diff --git a/assets/img/favicon/favicon.ico b/assets/img/favicon/favicon.ico deleted file mode 100644 index c4b19d5..0000000 Binary files a/assets/img/favicon/favicon.ico and /dev/null differ diff --git a/assets/img/favicon/ms-icon-144x144.png b/assets/img/favicon/ms-icon-144x144.png deleted file mode 100644 index 43a5a9e..0000000 Binary files a/assets/img/favicon/ms-icon-144x144.png and /dev/null differ diff --git a/assets/img/favicon/ms-icon-150x150.png b/assets/img/favicon/ms-icon-150x150.png deleted file mode 100644 index 5a8d487..0000000 Binary files a/assets/img/favicon/ms-icon-150x150.png and /dev/null differ diff --git a/assets/img/favicon/ms-icon-310x310.png b/assets/img/favicon/ms-icon-310x310.png deleted file mode 100644 index f7a262a..0000000 Binary files a/assets/img/favicon/ms-icon-310x310.png and /dev/null differ diff --git a/assets/img/favicon/ms-icon-70x70.png b/assets/img/favicon/ms-icon-70x70.png deleted file mode 100644 index 8871919..0000000 Binary files a/assets/img/favicon/ms-icon-70x70.png and /dev/null differ diff --git a/assets/img/logo-social.png b/assets/img/logo-social.png deleted file mode 100644 index a97caba..0000000 Binary files a/assets/img/logo-social.png and /dev/null differ diff --git a/assets/img/logo/center.svg b/assets/img/logo/center.svg deleted file mode 100644 index dc28bac..0000000 --- a/assets/img/logo/center.svg +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/img/logo/circle.svg b/assets/img/logo/circle.svg deleted file mode 100644 index fb67f48..0000000 --- a/assets/img/logo/circle.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/img/logo/comment.svg b/assets/img/logo/comment.svg deleted file mode 100644 index 8c3051a..0000000 --- a/assets/img/logo/comment.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/img/logo/debug.svg b/assets/img/logo/debug.svg deleted file mode 100644 index 9ea55ea..0000000 --- a/assets/img/logo/debug.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/img/logo/delete.svg b/assets/img/logo/delete.svg deleted file mode 100644 index b3f6f96..0000000 --- a/assets/img/logo/delete.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/img/logo/dollar.svg b/assets/img/logo/dollar.svg deleted file mode 100644 index 80c776e..0000000 --- a/assets/img/logo/dollar.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/img/logo/edit.svg b/assets/img/logo/edit.svg deleted file mode 100644 index 23f0650..0000000 --- a/assets/img/logo/edit.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/img/logo/hide.svg b/assets/img/logo/hide.svg deleted file mode 100644 index 7dda0aa..0000000 --- a/assets/img/logo/hide.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - image/svg+xml - - - - - - - eye-hide-line - - - - - diff --git a/assets/img/logo/info.svg b/assets/img/logo/info.svg deleted file mode 100644 index 7cc245a..0000000 --- a/assets/img/logo/info.svg +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/img/logo/photo.svg b/assets/img/logo/photo.svg deleted file mode 100644 index ebc0e4e..0000000 --- a/assets/img/logo/photo.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/img/logo/precision.svg b/assets/img/logo/precision.svg deleted file mode 100644 index 1114125..0000000 --- a/assets/img/logo/precision.svg +++ /dev/null @@ -1 +0,0 @@ -meter \ No newline at end of file diff --git a/assets/img/logo/star.svg b/assets/img/logo/star.svg deleted file mode 100644 index bdca924..0000000 --- a/assets/img/logo/star.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/assets/img/marker/cluster-icon-blue.png b/assets/img/marker/cluster-icon-blue.png deleted file mode 100644 index 06a9a60..0000000 Binary files a/assets/img/marker/cluster-icon-blue.png and /dev/null differ diff --git a/assets/img/marker/cluster-icon-green.png b/assets/img/marker/cluster-icon-green.png deleted file mode 100644 index 82f84e4..0000000 Binary files a/assets/img/marker/cluster-icon-green.png and /dev/null differ diff --git a/assets/img/marker/cluster-icon-red.png b/assets/img/marker/cluster-icon-red.png deleted file mode 100644 index 405c7e1..0000000 Binary files a/assets/img/marker/cluster-icon-red.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-black.png b/assets/img/marker/marker-icon-black.png deleted file mode 100644 index 23c94cf..0000000 Binary files a/assets/img/marker/marker-icon-black.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-blue.png b/assets/img/marker/marker-icon-blue.png deleted file mode 100644 index 0015b64..0000000 Binary files a/assets/img/marker/marker-icon-blue.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-gold.png b/assets/img/marker/marker-icon-gold.png deleted file mode 100644 index 6992d65..0000000 Binary files a/assets/img/marker/marker-icon-gold.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-green.png b/assets/img/marker/marker-icon-green.png deleted file mode 100644 index c359abb..0000000 Binary files a/assets/img/marker/marker-icon-green.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-grey.png b/assets/img/marker/marker-icon-grey.png deleted file mode 100644 index 43b3eb4..0000000 Binary files a/assets/img/marker/marker-icon-grey.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-orange.png b/assets/img/marker/marker-icon-orange.png deleted file mode 100644 index c3c8632..0000000 Binary files a/assets/img/marker/marker-icon-orange.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-red.png b/assets/img/marker/marker-icon-red.png deleted file mode 100644 index 1c26e9f..0000000 Binary files a/assets/img/marker/marker-icon-red.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-violet.png b/assets/img/marker/marker-icon-violet.png deleted file mode 100644 index ea748aa..0000000 Binary files a/assets/img/marker/marker-icon-violet.png and /dev/null differ diff --git a/assets/img/marker/marker-icon-yellow.png b/assets/img/marker/marker-icon-yellow.png deleted file mode 100644 index 8b677d9..0000000 Binary files a/assets/img/marker/marker-icon-yellow.png and /dev/null differ diff --git a/assets/img/marker/marker-shadow.png b/assets/img/marker/marker-shadow.png deleted file mode 100644 index 84c5808..0000000 Binary files a/assets/img/marker/marker-shadow.png and /dev/null differ diff --git a/assets/nls/de.json b/assets/nls/de.json deleted file mode 100644 index 29c0ea2..0000000 --- a/assets/nls/de.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "debug": { - "lat": "Latitude", - "lng": "Longitude", - "updates": "Updates", - "accuracy": "Accuracy", - "highAccuracy": "High accuracy", - "posAge": "Position max age", - "posTimeout": "Position timeout", - "zoom": "Zoom level", - "enabled": "Enabled", - "disabled": "Disabled", - "marks": "Marks", - "export": "Export data" - }, - "notif": { - "geolocationError": "Your browser doesn't implement the geolocation API", - "newMarkerOutside": "New marker out of your range", - "spotAdded": "New spot saved to map", - "storeAdded": "New store saved to map", - "barAdded": "New bar saved to map", - "spotDeleted": "Spot has been successfully deleted from map", - "storeDeleted": "Store has been successfully deleted from map", - "barDeleted": "Bar has been successfully deleted from map", - "markNameEmpty": "You didn't specified a name for the mark", - "lockFocusOn": "Centering and locking on your position", - "unlockFocusOn": "Position locking ended" - }, - "nav": { - "add": "Add", - "cancel": "Cancel", - "close": "Close", - "delete": "Delete" - }, - "map": { - "newTitle": "New marker", - "newSpot": "Add a spot", - "newStore": "Add a shop", - "newBar": "Add a bar", - "planLayerOSM": "Plan OSM", - "planLayerGeo": "Plan GeoPortail", - "satLayerEsri": "Satellite ESRI", - "satLayerGeo": "Satellite GeoPortail" - }, - "spot": { - "title": "New spot", - "subtitle": "A spot is a remarkable place to crack a beer ! Share it with the community, wether it is for the astonishing view of for whatever it is enjoyable to drink a beer!", - "nameLabel": "Name that spot", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "store": { - "title": "New store", - "subtitle": "The must have place to refill your beer stock. The more info you provide, the better you help your fellow beer crackerz!", - "nameLabel": "Store name", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "bar": { - "title": "New bar", - "subtitle": "A bar is a holly place where you can get some nicely colded draft beers!", - "nameLabel": "Bar name", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "modal": { - "userTitle": "User account", - "userAccuracyPref": "High precision", - "userDebugPref": "Debug interface", - "aboutTitle": "About BeerCrackerz", - "aboutDesc": "A brilliant idea from David Béché!
BeerCrackerz is the beer lovers comunity, filled with pint slayers and cereals lovers", - "hideShowTitle": "Map options", - "hideShowLabels": "Labels", - "hideShowCircles": "Circles", - "hideShowSpots": "Spots", - "hideShowStores": "Stores", - "hideShowBars": "Bars", - "deleteMarkTitle": "Delete mark", - "deleteMarkDesc": "Are you sure you want to delete this mark?
This action is permanent and can not be reverted.", - "spotEditTitle": "Edit spot", - "storeEditTitle": "Edit store", - "barEditTitle": "Edit bar" - }, - "popup": { - "spotFoundBy": "A spot discovered by", - "storeFoundBy": "A store added by", - "barFoundBy": "A bar added by", - "spotNoDesc": "No description available for this spot", - "storeNoDesc": "No description available for this store", - "barNoDesc": "No description available for this bar" - }, - "auth": { - "login": { - "headTitle": "Connexion | BeerCrackerz", - "subtitle": "Se connecter", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password": "Mot de passe", - "login": "Se connecter", - "notRegistered": "Pas encore inscrit?", - "register": "Créer un compte", - "bothEmpty": "Veuillez saisir un nom d'utilisateur et un mot de passe", - "usernameEmpty": "Veuillez saisir un nom d'utilisateur", - "passwordEmpty": "Veuillez saisir votre mot de passe", - "serverError": "Une erreur serveur est survenue, contactez le support" - }, - "register": { - "headTitle": "S'inscrire | BeerCrackerz", - "subtitle": "S'inscrire", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password1": "Mot de passe", - "password2": "Confirmer le mot de passe", - "register": "S'inscrire", - "notRegistered": "Déjà inscrit?", - "login": "Se connecter", - "fieldEmpty": "Veuillez remplir tous les champs du formulaire", - "notMatchingPassword": "Les deux mots de passe ne correspondent pas" - } - } -} diff --git a/assets/nls/en.json b/assets/nls/en.json deleted file mode 100644 index 848c375..0000000 --- a/assets/nls/en.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "debug": { - "lat": "Latitude", - "lng": "Longitude", - "updates": "Updates", - "accuracy": "Accuracy", - "highAccuracy": "High accuracy", - "posAge": "Position max age", - "posTimeout": "Position timeout", - "zoom": "Zoom level", - "enabled": "Enabled", - "disabled": "Disabled", - "marks": "Marks", - "export": "Export data" - }, - "notif": { - "geolocationError": "Your browser doesn't implement the geolocation API", - "newMarkerOutside": "New marker out of your range", - "spotAdded": "New spot saved to map", - "storeAdded": "New store saved to map", - "barAdded": "New bar saved to map", - "spotDeleted": "Spot has been successfully deleted from map", - "storeDeleted": "Store has been successfully deleted from map", - "barDeleted": "Bar has been successfully deleted from map", - "markNameEmpty": "You didn't specified a name for the mark", - "lockFocusOn": "Centering and locking view on your position", - "unlockFocusOn": "Position locking ended" - }, - "nav": { - "add": "Add", - "cancel": "Cancel", - "close": "Close", - "delete": "Delete" - }, - "map": { - "newTitle": "New marker", - "newSpot": "Add a spot", - "newStore": "Add a shop", - "newBar": "Add a bar", - "planLayerOSM": "Plan OSM", - "planLayerGeo": "Plan GeoPortail", - "satLayerEsri": "Satellite ESRI", - "satLayerGeo": "Satellite GeoPortail" - }, - "spot": { - "title": "New spot", - "subtitle": "A spot is a remarkable place to crack a beer ! Share it with the community, wether it is for the astonishing view of for whatever it is enjoyable to drink a beer!", - "nameLabel": "Name that spot", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "store": { - "title": "New store", - "subtitle": "The must have place to refill your beer stock. The more info you provide, the better you help your fellow beer crackerz!", - "nameLabel": "Store name", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "bar": { - "title": "New bar", - "subtitle": "A bar is a holly place where you can get some nicely colded draft beers!", - "nameLabel": "Bar name", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "modal": { - "userTitle": "User account", - "userAccuracyPref": "High precision", - "userDebugPref": "Debug interface", - "aboutTitle": "About BeerCrackerz", - "aboutDesc": "A brilliant idea from David Béché!
BeerCrackerz is the beer lovers comunity, filled with pint slayers and cereals lovers", - "hideShowTitle": "Map options", - "hideShowLabels": "Labels", - "hideShowCircles": "Circles", - "hideShowSpots": "Spots", - "hideShowStores": "Stores", - "hideShowBars": "Bars", - "deleteMarkTitle": "Delete mark", - "deleteMarkDesc": "Are you sure you want to delete this mark?
This action is permanent and can not be reverted.", - "spotEditTitle": "Edit spot", - "storeEditTitle": "Edit store", - "barEditTitle": "Edit bar" - }, - "popup": { - "spotFoundBy": "A spot discovered by", - "storeFoundBy": "A store added by", - "barFoundBy": "A bar added by", - "spotNoDesc": "No description available for this spot", - "storeNoDesc": "No description available for this store", - "barNoDesc": "No description available for this bar" - }, - "auth": { - "login": { - "headTitle": "Connexion | BeerCrackerz", - "subtitle": "Se connecter", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password": "Mot de passe", - "login": "Se connecter", - "notRegistered": "Pas encore inscrit?", - "register": "Créer un compte", - "bothEmpty": "Veuillez saisir un nom d'utilisateur et un mot de passe", - "usernameEmpty": "Veuillez saisir un nom d'utilisateur", - "passwordEmpty": "Veuillez saisir votre mot de passe", - "serverError": "Une erreur serveur est survenue, contactez le support" - }, - "register": { - "headTitle": "S'inscrire | BeerCrackerz", - "subtitle": "S'inscrire", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password1": "Mot de passe", - "password2": "Confirmer le mot de passe", - "register": "S'inscrire", - "notRegistered": "Déjà inscrit?", - "login": "Se connecter", - "fieldEmpty": "Veuillez remplir tous les champs du formulaire", - "notMatchingPassword": "Les deux mots de passe ne correspondent pas" - } - } -} diff --git a/assets/nls/es.json b/assets/nls/es.json deleted file mode 100644 index c7a8342..0000000 --- a/assets/nls/es.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "debug": { - "lat": "Latitude", - "lng": "Longitude", - "updates": "Updates", - "accuracy": "Accuracy", - "highAccuracy": "High accuracy", - "posAge": "Position max age", - "posTimeout": "Position timeout", - "zoom": "Zoom level", - "enabled": "Enabled", - "disabled": "Disabled", - "marks": "Marks", - "export": "Export data" - }, - "notif": { - "geolocationError": "Your browser doesn't implement the geolocation API", - "newMarkerOutside": "New marker out of range", - "spotAdded": "New spot saved to map", - "storeAdded": "New store saved to map", - "barAdded": "New bar saved to map", - "spotDeleted": "Spot has been successfully deleted from map", - "storeDeleted": "Store has been successfully deleted from map", - "barDeleted": "Bar has been successfully deleted from map", - "markNameEmpty": "You didn't specified a name for the mark", - "lockFocusOn": "Centering and locking on your position", - "unlockFocusOn": "Position locking ended" - }, - "nav": { - "add": "Add", - "cancel": "Cancel", - "close": "Close", - "delete": "Delete" - }, - "map": { - "newTitle": "New marker", - "newSpot": "Add a spot", - "newStore": "Add a shop", - "newBar": "Add a bar", - "planLayerOSM": "Plan OSM", - "planLayerGeo": "Plan GeoPortail", - "satLayerEsri": "Satellite ESRI", - "satLayerGeo": "Satellite GeoPortail" - }, - "spot": { - "title": "New spot", - "subtitle": "A spot is a remarkable place to crack a beer ! Share it with the community, wether it is for the astonishing view of for whatever it is enjoyable to drink a beer!", - "nameLabel": "Name that spot", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "store": { - "title": "New store", - "subtitle": "The must have place to refill your beer stock. The more info you provide, the better you help your fellow beer crackerz!", - "nameLabel": "Store name", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "bar": { - "title": "New bar", - "subtitle": "A bar is a holly place where you can get some nicely colded draft beers!", - "nameLabel": "Bar name", - "descLabel": "Why don't you describe it", - "rateLabel": "Give it a note" - }, - "modal": { - "userTitle": "User account", - "userAccuracyPref": "High precision", - "userDebugPref": "Debug interface", - "aboutTitle": "About BeerCrackerz", - "aboutDesc": "A brilliant idea from David Béché!
BeerCrackerz is the beer lovers comunity, filled with pint slayers and cereals lovers", - "hideShowTitle": "Map options", - "hideShowLabels": "Labels", - "hideShowCircles": "Circles", - "hideShowSpots": "Spots", - "hideShowStores": "Stores", - "hideShowBars": "Bars", - "deleteMarkTitle": "Delete mark", - "deleteMarkDesc": "Are you sure you want to delete this mark?
This action is permanent and can not be reverted.", - "spotEditTitle": "Edit spot", - "storeEditTitle": "Edit store", - "barEditTitle": "Edit bar" - }, - "popup": { - "spotFoundBy": "A spot discovered by", - "storeFoundBy": "A store added by", - "barFoundBy": "A bar added by", - "spotNoDesc": "No description available for this spot", - "storeNoDesc": "No description available for this store", - "barNoDesc": "No description available for this bar" - }, - "auth": { - "login": { - "headTitle": "Connexion | BeerCrackerz", - "subtitle": "Se connecter", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password": "Mot de passe", - "login": "Se connecter", - "notRegistered": "Pas encore inscrit?", - "register": "Créer un compte", - "bothEmpty": "Veuillez saisir un nom d'utilisateur et un mot de passe", - "usernameEmpty": "Veuillez saisir un nom d'utilisateur", - "passwordEmpty": "Veuillez saisir votre mot de passe", - "serverError": "Une erreur serveur est survenue, contactez le support" - }, - "register": { - "headTitle": "S'inscrire | BeerCrackerz", - "subtitle": "S'inscrire", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password1": "Mot de passe", - "password2": "Confirmer le mot de passe", - "register": "S'inscrire", - "notRegistered": "Déjà inscrit?", - "login": "Se connecter", - "fieldEmpty": "Veuillez remplir tous les champs du formulaire", - "notMatchingPassword": "Les deux mots de passe ne correspondent pas" - } - } -} diff --git a/assets/nls/fr.json b/assets/nls/fr.json deleted file mode 100644 index 571ad49..0000000 --- a/assets/nls/fr.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "debug": { - "lat": "Latitude", - "lng": "Longitude", - "updates": "Mises à jour", - "accuracy": "Précision", - "highAccuracy": "Haute précision", - "posAge": "Fréquence de rafraichissement", - "posTimeout": "Validité de la position", - "zoom": "Niveau de zoom", - "enabled": "Activée", - "disabled": "Désactivée", - "marks": "Marqueurs", - "export": "Exporter les données" - }, - "notif": { - "geolocationError": "Votre navigateur ne peux utiliser votre localisation", - "newMarkerOutside": "Nouveau marqueur hors de votre portée", - "spotAdded": "Nouveau spot ajouté à la carte", - "storeAdded": "Nouveau magasin ajouté à la carte", - "barAdded": "Nouveau bar ajouté à la carte", - "spotDeleted": "Le spot a été supprimé de la carte avec succès", - "storeDeleted": "Le magasin a été supprimé de la carte avec succès", - "barDeleted": "Le bar a été supprimé de la carte avec succès", - "markNameEmpty": "Vous devez specifier un nom pour le marqueur", - "lockFocusOn": "Suivre et recentrer sur votre position", - "unlockFocusOn": "Fin du suivi de position" - }, - "nav": { - "add": "Ajouter", - "cancel": "Annuler", - "close": "Fermer", - "delete": "Supprimer" - }, - "map": { - "newTitle": "Nouveau marqueur", - "newSpot": "Ajouter un spot", - "newStore": "Ajouter un magasin", - "newBar": "Ajouter un bar", - "planLayerOSM": "Plan OSM", - "planLayerGeo": "Plan GeoPortail", - "satLayerEsri": "Satellite ESRI", - "satLayerGeo": "Satellite GeoPortail" - }, - "spot": { - "title": "Nouveau spot", - "subtitle": "Un spot est un endroit remarquable pour cracker une bière en tout quiétude! Faites en profiter la communauté, que ce soit pour le calme exceptionnel, pour la vue incroyable ou pour tout autre source de ravissement houblonné.", - "nameLabel": "Nommer ce spot", - "descLabel": "Pourquoi ne pas le décrire", - "rateLabel": "Lui attribuer une note" - }, - "store": { - "title": "Nouveau magasin", - "subtitle": "C'est un indispensable pour se ravitailler des meilleurs breuvages houblonnés. Rensigner la gamme de prix et surtout si fraicheur il y a!", - "nameLabel": "Nom du magasin", - "descLabel": "Pourquoi ne pas le décrire", - "rateLabel": "Lui attribuer une note" - }, - "bar": { - "title": "Nouveau bar", - "subtitle": "Un bar est un endroit convivial ou le houblons coule des saintes tireuses à pression.", - "nameLabel": "Nom du bar", - "descLabel": "Pourquoi ne pas le décrire", - "rateLabel": "Lui attribuer une note" - }, - "modal": { - "userTitle": "Compte utilisateur", - "userAccuracyPref": "Haute précision", - "userDebugPref": "Interface de debug", - "aboutTitle": "À propos de BeerCrackerz", - "aboutDesc": "Une idée brillante de David Béché!
BeerCrackerz, c'est la communauté incontournable d'amoureux du houblons, de pourfendeurs de pintes, d'aficionados de céréales!", - "hideShowTitle": "Options de carte", - "hideShowLabels": "Étiquettes", - "hideShowCircles": "Cercles", - "hideShowSpots": "Spots", - "hideShowStores": "Magasins", - "hideShowBars": "Bars", - "deleteMarkTitle": "Supprimer le marqueur", - "deleteMarkDesc": "Êtes-vous sûr de vouloir supprimer ce marqueur?
Cette action est permanente et irréversible.", - "spotEditTitle": "Éditer le spot", - "storeEditTitle": "Éditer le magasin", - "barEditTitle": "Éditer le bar" - }, - "popup": { - "spotFoundBy": "Un spot découvert par", - "storeFoundBy": "Un magasin ajouté par", - "barFoundBy": "Un bar ajouté par", - "spotNoDesc": "Pas de description disponible pour ce spot", - "storeNoDesc": "Pas de description disponible pour ce magasin", - "barNoDesc": "Pas de description disponible pour ce bar" - }, - "auth": { - "login": { - "headTitle": "Connexion | BeerCrackerz", - "subtitle": "Se connecter", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password": "Mot de passe", - "login": "Se connecter", - "notRegistered": "Pas encore inscrit?", - "register": "Créer un compte", - "bothEmpty": "Veuillez saisir un nom d'utilisateur et un mot de passe", - "usernameEmpty": "Veuillez saisir un nom d'utilisateur", - "passwordEmpty": "Veuillez saisir votre mot de passe", - "serverError": "Une erreur serveur est survenue, contactez le support" - }, - "register": { - "headTitle": "S'inscrire | BeerCrackerz", - "subtitle": "S'inscrire", - "hiddenError": "Oh! Un texte caché!", - "username": "Nom d'utilisateur ou Email", - "password1": "Mot de passe", - "password2": "Confirmer le mot de passe", - "register": "S'inscrire", - "notRegistered": "Déjà inscrit?", - "login": "Se connecter", - "fieldEmpty": "Veuillez remplir tous les champs du formulaire", - "notMatchingPassword": "Les deux mots de passe ne correspondent pas" - } - } -} diff --git a/authindex.html b/authindex.html deleted file mode 100644 index 6f859be..0000000 --- a/authindex.html +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - Beer Crackerz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

-
- -
-
-

+

-
-
-
-

-
-
- - - - - - - - - diff --git a/back/api/__init__.py b/back/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/api/admin.py b/back/api/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/back/api/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/back/api/apps.py b/back/api/apps.py new file mode 100644 index 0000000..66656fd --- /dev/null +++ b/back/api/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApiConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'api' diff --git a/back/api/migrations/__init__.py b/back/api/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/api/permissions/__init__.py b/back/api/permissions/__init__.py new file mode 100644 index 0000000..c2ca3c6 --- /dev/null +++ b/back/api/permissions/__init__.py @@ -0,0 +1 @@ +from .is_owner_or_read_only import IsOwnerOrReadOnly diff --git a/back/api/permissions/is_owner_or_read_only.py b/back/api/permissions/is_owner_or_read_only.py new file mode 100644 index 0000000..8b1b6aa --- /dev/null +++ b/back/api/permissions/is_owner_or_read_only.py @@ -0,0 +1,9 @@ +from rest_framework.permissions import BasePermission, SAFE_METHODS + + +class IsOwnerOrReadOnly(BasePermission): + def has_object_permission(self, request, view, obj): + if request.method in SAFE_METHODS: + return True + + return obj.user == request.user diff --git a/back/api/serializers/__init__.py b/back/api/serializers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/api/serializers/bar/__init__.py b/back/api/serializers/bar/__init__.py new file mode 100644 index 0000000..daccb47 --- /dev/null +++ b/back/api/serializers/bar/__init__.py @@ -0,0 +1 @@ +from .bar import BarSerializer diff --git a/back/api/serializers/bar/bar.py b/back/api/serializers/bar/bar.py new file mode 100644 index 0000000..a33f67c --- /dev/null +++ b/back/api/serializers/bar/bar.py @@ -0,0 +1,37 @@ +from rest_framework import serializers + +from api.serializers.point.point import PointSerializer +from app.models.bar import Bar + + +class BarSerializer(PointSerializer): + types = serializers.MultipleChoiceField(choices=Bar.Types.choices) + modifiers = serializers.MultipleChoiceField(choices=Bar.Modifiers.choices, required=False) + price = serializers.IntegerField(min_value=0, max_value=2) + + # Read only fields + type = serializers.CharField(default='bar', read_only=True) + rate = serializers.SerializerMethodField() # TODO : Set required + user = serializers.CharField(source='user.username', read_only=True) + userId = serializers.IntegerField(source='user.id', read_only=True) + + class Meta: + model = Bar + fields = ( + 'id', 'type', 'name', 'description', 'lng', 'lat', 'rate', 'price', 'types', 'modifiers', 'user', 'userId', + 'creationDate') + + def create(self, validated_data): + user = self.context['request'].user + validated_data['user'] = user + return super().create(validated_data) + + def get_rate(self, obj): + # Call to Rate table + return 0.0 + + def validate_types(self, value): + if len(value) == 0: + raise serializers.ValidationError('TYPE_EMPTY') + + return value diff --git a/back/api/serializers/point/__init__.py b/back/api/serializers/point/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/api/serializers/point/point.py b/back/api/serializers/point/point.py new file mode 100644 index 0000000..4f0d077 --- /dev/null +++ b/back/api/serializers/point/point.py @@ -0,0 +1,5 @@ +from rest_framework import serializers + + +class PointSerializer(serializers.ModelSerializer): + creationDate = serializers.DateField(source='creation_date', read_only=True) diff --git a/back/api/serializers/shop/__init__.py b/back/api/serializers/shop/__init__.py new file mode 100644 index 0000000..2009914 --- /dev/null +++ b/back/api/serializers/shop/__init__.py @@ -0,0 +1 @@ +from .shop import ShopSerializer diff --git a/back/api/serializers/shop/shop.py b/back/api/serializers/shop/shop.py new file mode 100644 index 0000000..a280457 --- /dev/null +++ b/back/api/serializers/shop/shop.py @@ -0,0 +1,37 @@ +from rest_framework import serializers + +from api.serializers.point.point import PointSerializer +from app.models.shop import Shop + + +class ShopSerializer(PointSerializer): + types = serializers.MultipleChoiceField(choices=Shop.Types.choices) + modifiers = serializers.MultipleChoiceField(choices=Shop.Modifiers.choices, required=False) + price = serializers.IntegerField(min_value=0, max_value=2) + + # Read only fields + type = serializers.CharField(default='shop', read_only=True) + rate = serializers.SerializerMethodField() # TODO : Set required + user = serializers.CharField(source='user.username', read_only=True) + userId = serializers.IntegerField(source='user.id', read_only=True) + + class Meta: + model = Shop + fields = ( + 'id', 'type', 'name', 'description', 'lng', 'lat', 'rate', 'price', 'types', 'modifiers', 'user', 'userId', + 'creationDate') + + def create(self, validated_data): + user = self.context['request'].user + validated_data['user'] = user + return super().create(validated_data) + + def get_rate(self, obj): + # Call to Rate table + return 0.0 + + def validate_types(self, value): + if len(value) == 0: + raise serializers.ValidationError('TYPE_EMPTY') + + return value diff --git a/back/api/serializers/spot/__init__.py b/back/api/serializers/spot/__init__.py new file mode 100644 index 0000000..96f9310 --- /dev/null +++ b/back/api/serializers/spot/__init__.py @@ -0,0 +1 @@ +from .spot import SpotSerializer diff --git a/back/api/serializers/spot/spot.py b/back/api/serializers/spot/spot.py new file mode 100644 index 0000000..b0b1f87 --- /dev/null +++ b/back/api/serializers/spot/spot.py @@ -0,0 +1,36 @@ +from rest_framework import serializers + +from api.serializers.point.point import PointSerializer +from app.models.spot import Spot + + +class SpotSerializer(PointSerializer): + types = serializers.MultipleChoiceField(choices=Spot.Types.choices) + modifiers = serializers.MultipleChoiceField(choices=Spot.Modifiers.choices, required=False) + + # Read only fields + type = serializers.CharField(default='spot', read_only=True) + rate = serializers.SerializerMethodField() # TODO : Set required + user = serializers.CharField(source='user.username', read_only=True) + userId = serializers.IntegerField(source='user.id', read_only=True) + + class Meta: + model = Spot + fields = ( + 'id', 'type', 'name', 'description', 'lng', 'lat', 'rate', 'types', 'modifiers', 'user', 'userId', + 'creationDate') + + def create(self, validated_data): + user = self.context['request'].user + validated_data['user'] = user + return super().create(validated_data) + + def get_rate(self, obj): + # Call to Rate table + return 0.0 + + def validate_types(self, value): + if len(value) == 0: + raise serializers.ValidationError('TYPE_EMPTY') + + return value diff --git a/back/api/serializers/user/__init__.py b/back/api/serializers/user/__init__.py new file mode 100644 index 0000000..c6b1ea2 --- /dev/null +++ b/back/api/serializers/user/__init__.py @@ -0,0 +1,3 @@ +from .user import UserSerializer +from .user_profile_picture import UserProfilePictureSerializer +from .user_register import UserRegisterSerializer diff --git a/back/api/serializers/user/user.py b/back/api/serializers/user/user.py new file mode 100644 index 0000000..a5fcf2e --- /dev/null +++ b/back/api/serializers/user/user.py @@ -0,0 +1,12 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers + + +class UserSerializer(serializers.ModelSerializer): + isActive = serializers.BooleanField(source='is_active') + isStaff = serializers.BooleanField(source='is_staff') + profilePicture = serializers.ImageField(source='profile_picture') + + class Meta: + model = get_user_model() + fields = ('id', 'username', 'email', 'isActive', 'isStaff', 'profilePicture') diff --git a/back/api/serializers/user/user_profile_picture.py b/back/api/serializers/user/user_profile_picture.py new file mode 100644 index 0000000..834c52c --- /dev/null +++ b/back/api/serializers/user/user_profile_picture.py @@ -0,0 +1,47 @@ +import base64 +import io +import uuid + +from PIL import Image +from rest_framework import serializers + +from api.services.image import resize_image, compress_image, crop_image + + +class UserProfilePictureSerializer(serializers.Serializer): + profile_picture = serializers.CharField() + minX = serializers.IntegerField(allow_null=True) + minY = serializers.IntegerField(allow_null=True) + maxX = serializers.IntegerField(allow_null=True) + maxY = serializers.IntegerField(allow_null=True) + + def save(self): + image = self.validated_data.get('profile_picture') + box = self.validated_data.get('box') + cropped_image = crop_image(image, box) + resized_image = resize_image(cropped_image) + return compress_image(resized_image, name=f'${uuid.uuid4().hex}.webp') + + def validate_profile_picture(self, value): + b64 = base64.b64decode(value[value.find('base64,') + len('base64,'):]) # Keep only base64 information + buffer = io.BytesIO(b64) + image = Image.open(buffer) + return image + + def validate(self, data): + image = data.get('profile_picture') + width, height = image.size + minX, minY, maxX, maxY = data.pop('minX'), data.pop('minY'), data.pop('maxX'), data.pop('maxY') + + # TODO : see to raise more specific error code + if width < 512 or height < 512: + raise serializers.ValidationError('PROFILE_PICTURE_SIZE_ERROR') + if maxX - minX < 512 or maxY - minY < 512: + raise serializers.ValidationError('PROFILE_PICTURE_SIZE_ERROR') + if maxX - minX != maxY - minY: + raise serializers.ValidationError('PROFILE_PICTURE_DIMENSION_ERROR') # Picture is not a square + if maxX > width or maxY > height: + raise serializers.ValidationError('PROFILE_PICTURE_DIMENSION_ERROR') + + data['box'] = (minX, minY, maxX, maxY) + return data diff --git a/back/api/serializers/user/user_register.py b/back/api/serializers/user/user_register.py new file mode 100644 index 0000000..1505ff6 --- /dev/null +++ b/back/api/serializers/user/user_register.py @@ -0,0 +1,41 @@ +from django.contrib.auth import get_user_model +from django.contrib.auth.password_validation import validate_password +from rest_framework import serializers +from rest_framework.validators import UniqueValidator + +from app.services.email import EmailService + + +class UserRegisterSerializer(serializers.Serializer): + username = serializers.CharField(max_length=50, + validators=[UniqueValidator(queryset=get_user_model().objects.all())]) + email = serializers.EmailField(max_length=264, + validators=[UniqueValidator(queryset=get_user_model().objects.all())]) + password1 = serializers.CharField(max_length=64) + password2 = serializers.CharField(max_length=64) + is_active = serializers.HiddenField(default=False) + + def create(self, validated_data): + user = get_user_model()(**validated_data) + password = validated_data.get('password') + validate_password(password=password, user=user) + + user.set_password(password) + user.save() + + EmailService.send_user_creation_email(user) + return user + + def to_representation(self, instance): + data = {'username': instance.username, 'email': instance.email} + return data + + def validate(self, data): + password1 = data.pop('password1') + password2 = data.pop('password2') + + if password1 != password2: + raise serializers.ValidationError('PASSWORD_NOT_MATCH') + + data['password'] = password1 + return data diff --git a/back/api/services/__init__.py b/back/api/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/api/services/image.py b/back/api/services/image.py new file mode 100644 index 0000000..9bfc523 --- /dev/null +++ b/back/api/services/image.py @@ -0,0 +1,22 @@ +import io +import sys + +from PIL import Image +from django.core.files.uploadedfile import InMemoryUploadedFile + + +def crop_image(image, box): + return image.crop(box) + + +def resize_image(image, size=(512, 512)): + return image.resize(size, Image.ANTIALIAS) + + +def compress_image(image, name, quality=80, webp_method=6): + output_io_stream = io.BytesIO() + image.save(output_io_stream, format='webp', method=webp_method, quality=quality) + output_io_stream.seek(0) + + return InMemoryUploadedFile(output_io_stream, 'ImageField', name, 'image/webp', sys.getsizeof(output_io_stream), + None) diff --git a/back/api/tests.py b/back/api/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/back/api/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/back/api/urls.py b/back/api/urls.py new file mode 100644 index 0000000..1c63e89 --- /dev/null +++ b/back/api/urls.py @@ -0,0 +1,23 @@ +from django.urls import include, path +from rest_framework import routers + +from api.views import UserViewSet, SpotViewSet, ShopViewSet, BarViewSet, user_connected, UserRegisterView +from api.views.auth import LoginView, LogoutView, ActivationView, PasswordResetRequest, PasswordReset + +router = routers.DefaultRouter() +router.register(r'user', UserViewSet, basename='user') +router.register(r'spot', SpotViewSet, basename='spot') +router.register(r'shop', ShopViewSet, basename='shop') +router.register(r'bar', BarViewSet, basename='bar') + +urlpatterns = [ + path('user/me/', user_connected, name='user_connected'), + path('', include(router.urls)), + path('auth/login/', LoginView.as_view(), name='login'), + path('auth/logout/', LogoutView.as_view(), name='logout'), + path('auth/register/', UserRegisterView.as_view(), name='register'), + path('auth/activation/', ActivationView.as_view(), name='user-activation'), + path('auth/password-reset-request/', PasswordResetRequest.as_view(), name='password-reset-request'), + path('auth/password-reset/', PasswordReset.as_view(), name='password-reset-request'), + path('api-auth/', include('rest_framework.urls', namespace='rest_framework')), +] diff --git a/back/api/views/__init__.py b/back/api/views/__init__.py new file mode 100644 index 0000000..0076502 --- /dev/null +++ b/back/api/views/__init__.py @@ -0,0 +1,4 @@ +from .bar import BarViewSet +from .shop import ShopViewSet +from .spot import SpotViewSet +from .user import UserViewSet, UserRegisterView, user_connected diff --git a/back/api/views/auth.py b/back/api/views/auth.py new file mode 100644 index 0000000..849564e --- /dev/null +++ b/back/api/views/auth.py @@ -0,0 +1,124 @@ +from django.contrib.auth import authenticate, login, logout, get_user_model +from django.contrib.auth.password_validation import validate_password +from django.shortcuts import redirect +from django.urls import reverse +from django.utils.decorators import method_decorator +from django.views import View +from django.views.decorators.cache import never_cache +from rest_framework import status +from rest_framework.exceptions import ParseError +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.response import Response +from rest_framework.views import APIView + +from app.services.email import EmailService +from app.utils.token import decode_uid, check_token + + +class LoginView(APIView): + permission_classes = [AllowAny] + + # @method_decorator(sensitive_post_parameters()) + # @method_decorator(csrf_protect) + @method_decorator(never_cache) + def post(self, request): + username = request.data['username'] + password = request.data['password'] + if not username or not password: + raise ParseError() + + user = authenticate(request, username=username, password=password) + if user is not None: + login(request, user) + else: + return Response(status=status.HTTP_401_UNAUTHORIZED, data={'error': 'userNotRegister'}) + + return Response(status=status.HTTP_200_OK, data={'status': 200}) + + +class LogoutView(APIView): + permission_classes = [IsAuthenticated] + + @method_decorator(never_cache) + def post(self, request): + logout(request) + return Response(status=status.HTTP_200_OK, data={'status': 200}) + + +class ActivationView(View): + permission_classes = [AllowAny] + + def get(self, request): + user_model = get_user_model() + + uidb64, token = request.GET.get('uidb64'), request.GET.get('token') + if not uidb64 or not token: + return self.redirect_to_welcome(activate=False) + + try: + uid = decode_uid(uidb64) + user = user_model.objects.get(pk=uid) + except(TypeError, ValueError, OverflowError, user_model.DoesNotExist): + return self.redirect_to_welcome(activate=False) + + if check_token(user, token): + user.is_active = True + user.save() + return self.redirect_to_welcome(activate=True) + else: + return self.redirect_to_welcome(activate=False) + + def redirect_to_welcome(self, activate): + url = f'{reverse("welcome")}?activate={activate}' + return redirect(url) + + +class PasswordResetRequest(APIView): + permission_classes = [AllowAny] + + def post(self, request): + user_model = get_user_model() + + email = request.data.get('email') + if not email: + raise ParseError() + + try: + user = user_model.objects.get(email=email) + except user_model.DoesNotExist: + user = None + + if user is not None: + EmailService.send_reset_password_email(user) + + return Response(status=status.HTTP_204_NO_CONTENT) + + +class PasswordReset(APIView): + permission_classes = [AllowAny] + + def post(self, request): + user_model = get_user_model() + + uidb64, token = request.query_params.get('uidb64'), request.query_params.get('token') + password1, password2 = request.data.get('password1'), request.data.get('password2') + if not uidb64 or not token or not password1 or not password2: + raise ParseError() + + if password1 != password2: + raise ParseError() + + try: + uid = decode_uid(uidb64) + user = user_model.objects.get(pk=uid) + except(TypeError, ValueError, OverflowError, user_model.DoesNotExist): + raise ParseError() + + if check_token(user, token): + validate_password(password=password1, user=user) + user.set_password(password1) + user.save() + # TODO : See if we send a confirmation mail + return Response(status=status.HTTP_204_NO_CONTENT) + else: + return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/back/api/views/bar.py b/back/api/views/bar.py new file mode 100644 index 0000000..e632336 --- /dev/null +++ b/back/api/views/bar.py @@ -0,0 +1,12 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAdminUser + +from api.permissions import IsOwnerOrReadOnly +from api.serializers.bar import BarSerializer +from app.models.bar import Bar + + +class BarViewSet(viewsets.ModelViewSet): + queryset = Bar.objects.all() + permission_classes = [IsOwnerOrReadOnly | IsAdminUser] + serializer_class = BarSerializer diff --git a/back/api/views/shop.py b/back/api/views/shop.py new file mode 100644 index 0000000..365da06 --- /dev/null +++ b/back/api/views/shop.py @@ -0,0 +1,12 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAdminUser + +from api.permissions import IsOwnerOrReadOnly +from api.serializers.shop import ShopSerializer +from app.models.shop import Shop + + +class ShopViewSet(viewsets.ModelViewSet): + queryset = Shop.objects.all() + permission_classes = [IsOwnerOrReadOnly | IsAdminUser] + serializer_class = ShopSerializer diff --git a/back/api/views/spot.py b/back/api/views/spot.py new file mode 100644 index 0000000..7d8e571 --- /dev/null +++ b/back/api/views/spot.py @@ -0,0 +1,12 @@ +from rest_framework import viewsets +from rest_framework.permissions import IsAdminUser + +from api.permissions import IsOwnerOrReadOnly +from api.serializers.spot import SpotSerializer +from app.models.spot import Spot + + +class SpotViewSet(viewsets.ModelViewSet): + queryset = Spot.objects.all() + permission_classes = [IsOwnerOrReadOnly | IsAdminUser] + serializer_class = SpotSerializer diff --git a/back/api/views/user.py b/back/api/views/user.py new file mode 100644 index 0000000..26d2403 --- /dev/null +++ b/back/api/views/user.py @@ -0,0 +1,37 @@ +from rest_framework import viewsets, status +from rest_framework.decorators import api_view, permission_classes, action +from rest_framework.generics import CreateAPIView +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.response import Response + +from api.serializers.user import UserSerializer, UserRegisterSerializer, UserProfilePictureSerializer +from app.models import User + + +class UserViewSet(viewsets.ReadOnlyModelViewSet): + queryset = User.objects.all() + serializer_class = UserSerializer + permission_classes = [IsAuthenticated] + + @action(detail=True, methods=['patch'], url_path='profile-picture', url_name='user-profile-picture') + def profile_picture(self, request, pk): + user = self.get_object() + serializer = UserProfilePictureSerializer(data=request.data) + if serializer.is_valid(raise_exception=True): + user.profile_picture = serializer.save() + user.save() + return Response(status=status.HTTP_204_NO_CONTENT) + else: + return Response(status=status.HTTP_400_BAD_REQUEST) + + +class UserRegisterView(CreateAPIView): + serializer_class = UserRegisterSerializer + permission_classes = [AllowAny] + + +@api_view() +@permission_classes([IsAuthenticated]) +def user_connected(request): + user = UserSerializer(request.user) + return Response(user.data) diff --git a/back/app/__init__.py b/back/app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/app/admin.py b/back/app/admin.py new file mode 100644 index 0000000..d2ed426 --- /dev/null +++ b/back/app/admin.py @@ -0,0 +1,30 @@ +from django.contrib import admin + +from app.models import User +from app.models.bar import Bar +from app.models.shop import Shop +from app.models.spot import Spot + +# Register your models here. +admin.site.register(User) + + +class SpotAdmin(admin.ModelAdmin): + list_display = ('pk', 'name', 'user', 'types', 'modifiers', 'creation_date') + + +admin.site.register(Spot, SpotAdmin) + + +class BarAdmin(admin.ModelAdmin): + list_display = ('pk', 'name', 'user', 'types', 'modifiers', 'creation_date') + + +admin.site.register(Bar, BarAdmin) + + +class ShopAdmin(admin.ModelAdmin): + list_display = ('pk', 'name', 'user', 'types', 'modifiers', 'creation_date') + + +admin.site.register(Shop, ShopAdmin) diff --git a/back/app/apps.py b/back/app/apps.py new file mode 100644 index 0000000..ed327d2 --- /dev/null +++ b/back/app/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class AppConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'app' diff --git a/back/app/management/__init__.py b/back/app/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/app/management/commands/__init__.py b/back/app/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/app/management/commands/backupdb.py b/back/app/management/commands/backupdb.py new file mode 100644 index 0000000..3badd0b --- /dev/null +++ b/back/app/management/commands/backupdb.py @@ -0,0 +1,17 @@ +import io + +from django.core.management import call_command +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + def handle(self, *args, **kwargs): + backup = io.StringIO() + call_command('dumpdata', + all=True, + format='json', + natural_foreign=True, + natural_primary=True, + stdout=backup) + + self.stdout.write(backup.getvalue()) diff --git a/back/app/management/commands/wait_for_db.py b/back/app/management/commands/wait_for_db.py new file mode 100644 index 0000000..70e6abf --- /dev/null +++ b/back/app/management/commands/wait_for_db.py @@ -0,0 +1,26 @@ +""" +Django command to wait for the database to be available +""" +import time + +from psycopg2 import OperationalError as Psycopg2OpError + +from django.db.utils import OperationalError +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + """Django command to wait for the database""" + def handle(self, *args, **kwargs): + """Entrypoint for command""" + self.stdout.write('Waiting for database...') + db_up = False + while db_up is False: + try: + self.check(databases=['default']) + db_up = True + except (Psycopg2OpError, OperationalError): + self.stdout.write('Database unavailable, waiting 1 second...') + time.sleep(1) + + self.stdout.write(self.style.SUCCESS('Database ready!')) \ No newline at end of file diff --git a/back/app/migrations/0001_release_0_1.py b/back/app/migrations/0001_release_0_1.py new file mode 100644 index 0000000..0c0d2c3 --- /dev/null +++ b/back/app/migrations/0001_release_0_1.py @@ -0,0 +1,132 @@ +# Generated by Django 4.1.2 on 2023-04-08 13:29 + +import django.contrib.auth.models +import django.contrib.auth.validators +import django.utils.timezone +import multiselectfield.db.fields +from django.conf import settings +from django.db import migrations, models + +import app.models.user + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='User', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, + help_text='Designates that this user has all permissions without explicitly assigning them.', + verbose_name='superuser status')), + ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, + help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', + max_length=150, unique=True, + validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], + verbose_name='username')), + ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), + ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), + ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), + ('is_staff', models.BooleanField(default=False, + help_text='Designates whether the user can log into this admin site.', + verbose_name='staff status')), + ('is_active', models.BooleanField(default=True, + help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', + verbose_name='active')), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), + ('profile_picture', models.ImageField(blank=True, null=True, upload_to='profile_picture/')), + ('groups', models.ManyToManyField(blank=True, + help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', + related_name='user_set', related_query_name='user', to='auth.group', + verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', + related_name='user_set', related_query_name='user', + to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'verbose_name': 'user', + 'verbose_name_plural': 'users', + 'abstract': False, + }, + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.CreateModel( + name='Shop', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('description', models.CharField(blank=True, max_length=500, null=True)), + ('lng', models.FloatField()), + ('lat', models.FloatField()), + ('creation_date', models.DateField(auto_now_add=True)), + ('modifiers', multiselectfield.db.fields.MultiSelectField( + choices=[('bio', 'Bio'), ('craft', 'Craft'), ('fresh', 'Fresh'), ('card', 'Card'), + ('choice', 'Choice')], max_length=100)), + ('types', multiselectfield.db.fields.MultiSelectField( + choices=[('store', 'Store'), ('super', 'Super'), ('hyper', 'Hyper'), ('cellar', 'Cellar')], + max_length=100)), + ('price', models.PositiveIntegerField()), + ('user', models.ForeignKey(editable=False, on_delete=models.SET(app.models.user.get_default_user), + related_name='shops', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Bar', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('description', models.CharField(blank=True, max_length=500, null=True)), + ('lng', models.FloatField()), + ('lat', models.FloatField()), + ('creation_date', models.DateField(auto_now_add=True)), + ('modifiers', multiselectfield.db.fields.MultiSelectField( + choices=[('tobacco', 'Tobacco'), ('food', 'Food'), ('card', 'Card'), ('choice', 'Choice'), + ('outdoor', 'Outdoor')], max_length=100)), + ('types', multiselectfield.db.fields.MultiSelectField( + choices=[('regular', 'Regular'), ('snack', 'Snack'), ('cellar', 'Cellar'), ('rooftop', 'Rooftop')], + max_length=100)), + ('price', models.PositiveIntegerField()), + ('user', models.ForeignKey(editable=False, on_delete=models.SET(app.models.user.get_default_user), + related_name='bar', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Spot', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('description', models.CharField(blank=True, max_length=500, null=True)), + ('lng', models.FloatField()), + ('lat', models.FloatField()), + ('creation_date', models.DateField(auto_now_add=True)), + ('modifiers', multiselectfield.db.fields.MultiSelectField( + choices=[('bench', 'Bench'), ('covered', 'Covered'), ('toilet', 'Toilet'), ('store', 'Store'), + ('trash', 'Trash'), ('parking', 'Parking')], max_length=100)), + ('types', multiselectfield.db.fields.MultiSelectField( + choices=[('forest', 'Forest'), ('river', 'River'), ('lake', 'Lake'), ('cliff', 'Cliff'), + ('mountain', 'Mountain'), ('beach', 'Beach'), ('sea', 'Sea'), ('city', 'City'), + ('pov', 'Pov')], max_length=100)), + ('user', models.ForeignKey(editable=False, on_delete=models.SET(app.models.user.get_default_user), + related_name='spots', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/back/app/migrations/__init__.py b/back/app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/app/models/__init__.py b/back/app/models/__init__.py new file mode 100644 index 0000000..ee4c00b --- /dev/null +++ b/back/app/models/__init__.py @@ -0,0 +1 @@ +from .user import User diff --git a/back/app/models/bar.py b/back/app/models/bar.py new file mode 100644 index 0000000..dc2c741 --- /dev/null +++ b/back/app/models/bar.py @@ -0,0 +1,29 @@ +from django.conf import settings +from django.db import models +from multiselectfield import MultiSelectField + +from .point import Point +from .user import get_default_user + + +class Bar(Point): + class Modifiers(models.TextChoices): + TOBACCO = 'tobacco' + FOOD = 'food' + CARD = 'card' + CHOICE = 'choice' + OUTDOOR = 'outdoor' + + class Types(models.TextChoices): + REGULAR = 'regular' + SNACK = 'snack' + CELLAR = 'cellar' + ROOFTOP = 'rooftop' + + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_default_user), related_name='bar', + editable=False) + # Set max_length because max_length calcul is broken https://github.com/goinnn/django-multiselectfield/issues/131 + modifiers = MultiSelectField(choices=Modifiers.choices, max_length=100) + # Set max_length because max_length calcul is broken https://github.com/goinnn/django-multiselectfield/issues/131 + types = MultiSelectField(choices=Types.choices, max_length=100) + price = models.PositiveIntegerField() diff --git a/back/app/models/point.py b/back/app/models/point.py new file mode 100644 index 0000000..07b34e7 --- /dev/null +++ b/back/app/models/point.py @@ -0,0 +1,12 @@ +from django.db import models + + +class Point(models.Model): + name = models.CharField(max_length=50) + description = models.CharField(max_length=500, null=True, blank=True) + lng = models.FloatField() + lat = models.FloatField() + creation_date = models.DateField(auto_now_add=True) + + class Meta: + abstract = True diff --git a/back/app/models/shop.py b/back/app/models/shop.py new file mode 100644 index 0000000..23a76f3 --- /dev/null +++ b/back/app/models/shop.py @@ -0,0 +1,29 @@ +from django.conf import settings +from django.db import models +from multiselectfield import MultiSelectField + +from .point import Point +from .user import get_default_user + + +class Shop(Point): + class Modifiers(models.TextChoices): + BIO = 'bio' + CRAFT = 'craft' + FRESH = 'fresh' + CARD = 'card' + CHOICE = 'choice' + + class Types(models.TextChoices): + STORE = 'store' + SUPER = 'super' + HYPER = 'hyper' + CELLAR = 'cellar' + + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_default_user), related_name='shops', + editable=False) + # Set max_length because max_length calcul is broken https://github.com/goinnn/django-multiselectfield/issues/131 + modifiers = MultiSelectField(choices=Modifiers.choices, max_length=100) + # Set max_length because max_length calcul is broken https://github.com/goinnn/django-multiselectfield/issues/131 + types = MultiSelectField(choices=Types.choices, max_length=100) + price = models.PositiveIntegerField() diff --git a/back/app/models/spot.py b/back/app/models/spot.py new file mode 100644 index 0000000..e8c63a3 --- /dev/null +++ b/back/app/models/spot.py @@ -0,0 +1,34 @@ +from django.conf import settings +from django.db import models +from multiselectfield import MultiSelectField + +from .point import Point +from .user import get_default_user + + +class Spot(Point): + class Modifiers(models.TextChoices): + BENCH = 'bench' + COVERED = 'covered' + TOILET = 'toilet' + STORE = 'store' + TRASH = 'trash' + PARKING = 'parking' + + class Types(models.TextChoices): + FOREST = 'forest' + RIVER = 'river' + LAKE = 'lake' + CLIFF = 'cliff' + MOUNTAIN = 'mountain' + BEACH = 'beach' + SEA = 'sea' + CITY = 'city' + POV = 'pov' + + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_default_user), related_name='spots', + editable=False) + # Set max_length because max_length calcul is broken https://github.com/goinnn/django-multiselectfield/issues/131 + modifiers = MultiSelectField(choices=Modifiers.choices, max_length=100) + # Set max_length because max_length calcul is broken https://github.com/goinnn/django-multiselectfield/issues/131 + types = MultiSelectField(choices=Types.choices, max_length=100) diff --git a/back/app/models/user.py b/back/app/models/user.py new file mode 100644 index 0000000..fce2e29 --- /dev/null +++ b/back/app/models/user.py @@ -0,0 +1,35 @@ +import os + +from django.contrib.auth import get_user_model +from django.contrib.auth.models import AbstractUser +from django.db import models +from django.dispatch import receiver + + +class User(AbstractUser): + profile_picture = models.ImageField(upload_to='profile_picture/', null=True, blank=True) + + +@receiver(models.signals.pre_save, sender=User) +def pre_save_profile_picture(sender, instance, **kwargs): + try: + old_profile_picture = sender.objects.get(id=instance.id).profile_picture + except sender.DoesNotExist: + old_profile_picture = None + new_profile_picture = instance.profile_picture + if not old_profile_picture or not new_profile_picture: + return + + if old_profile_picture.path != new_profile_picture.path: + if os.path.isfile(old_profile_picture.path): + os.remove(old_profile_picture.path) + + +@receiver(models.signals.post_delete, sender=User) +def post_delete_profile_picture(sender, instance, **kwargs): + if instance.profile_picture: + instance.profile_picture.delete(save=False) + + +def get_default_user(): + return get_user_model().objetcs.get_or_create(username="Deleted user")[0] diff --git a/back/app/services/__init__.py b/back/app/services/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/app/services/email.py b/back/app/services/email.py new file mode 100644 index 0000000..ea86fad --- /dev/null +++ b/back/app/services/email.py @@ -0,0 +1,57 @@ +import threading + +from django.conf import settings +from django.core.mail import EmailMultiAlternatives +from django.template.loader import get_template +from django.urls import reverse + +from app.utils.token import get_token_from_user, encode_uid + + +class EmailService: + @staticmethod + def _send_mail(**kwargs): + html_body = kwargs.pop('html_body') + email = EmailMultiAlternatives(**kwargs) + if html_body: + email.attach_alternative(html_body, 'text/html') + + email.send() + + @staticmethod + def _send_mail_async(**kwargs): + threading.Thread(target=EmailService._send_mail, kwargs=kwargs).start() + + @staticmethod + def send_user_creation_email(user): + uidb64 = encode_uid(user.pk) + token = get_token_from_user(user) + link = f'{settings.SERVER_URL}{reverse("user-activation")}?uidb64={uidb64}&token={token}' + context = {'user': user, 'link': link} + + html_template = get_template('email/html/user-creation.html') + text_template = get_template('email/text/user-creation.txt') + html_body = html_template.render(context) + text_body = text_template.render(context) + + subject = 'Beer Crackerz - Création de compte' + to = (user.email,) + + EmailService._send_mail_async(subject=subject, to=to, body=text_body, html_body=html_body) + + @staticmethod + def send_reset_password_email(user): + uidb64 = encode_uid(user.pk) + token = get_token_from_user(user) + link = f'{settings.SERVER_URL}{reverse("welcome")}?uidb64={uidb64}&token={token}' + context = {'user': user, 'link': link} + + html_template = get_template('email/html/password-reset.html') + text_template = get_template('email/text/password-reset.txt') + html_body = html_template.render(context) + text_body = text_template.render(context) + + subject = 'Beer Crackerz - Réinitialisation de mot de passe' + to = (user.email,) + + EmailService._send_mail_async(subject=subject, to=to, body=text_body, html_body=html_body) diff --git a/back/app/templates/aside/forgot-password.html b/back/app/templates/aside/forgot-password.html new file mode 100644 index 0000000..f613fd9 --- /dev/null +++ b/back/app/templates/aside/forgot-password.html @@ -0,0 +1,17 @@ +{% load static %} +
+
+

BeerCrackerz

+

{FORGOT_PASSWORD_SUBTITLE}

+
+
+

{FORGOT_PASSWORD_ERROR}

+ + + +

{FORGOT_PASSWORD_LOGIN_LABEL} {FORGOT_PASSWORD_LOGIN}

+
+ diff --git a/back/app/templates/aside/login.html b/back/app/templates/aside/login.html new file mode 100644 index 0000000..c328887 --- /dev/null +++ b/back/app/templates/aside/login.html @@ -0,0 +1,20 @@ +{% load static %} +
+
+

BeerCrackerz

+

{LOGIN_SUBTITLE}

+
+
+ + + + + + +

{LOGIN_NOT_REGISTERED} {LOGIN_REGISTER}

+

{LOGIN_FORGOT_PASSWORD} {LOGIN_PASSWORD_RESET}

+
+ diff --git a/back/app/templates/aside/register.html b/back/app/templates/aside/register.html new file mode 100644 index 0000000..167b726 --- /dev/null +++ b/back/app/templates/aside/register.html @@ -0,0 +1,23 @@ +{% load static %} +
+
+

BeerCrackerz

+

{REGISTER_SUBTITLE}

+
+
+ + + + + + + + + + +

{REGISTER_ALREADY_DONE} {REGISTER_LOGIN}

+
+ diff --git a/back/app/templates/aside/reset-password.html b/back/app/templates/aside/reset-password.html new file mode 100644 index 0000000..9489cbb --- /dev/null +++ b/back/app/templates/aside/reset-password.html @@ -0,0 +1,19 @@ +{% load static %} +
+
+

BeerCrackerz

+

{RESET_PASSWORD_SUBTITLE}

+
+
+

{RESET_PASSWORD_HIDDEN_ERROR}

+ + + + + +

{RESET_PASSWORD_LOGIN_LABEL} {RESET_PASSWORD_LOGIN}

+
+ diff --git a/back/app/templates/email/html/password-reset.html b/back/app/templates/email/html/password-reset.html new file mode 100644 index 0000000..a6bbac4 --- /dev/null +++ b/back/app/templates/email/html/password-reset.html @@ -0,0 +1,24 @@ +{% autoescape off %} +

+ + Bonjour {{ user.username | capfirst }}, + + +

+

 

+

+ Une demande de réinitialisation de votre mot de passe viens d'être effectuée. Pour changer votre mot de passe, veuillez cliquer sur le lien suivant : +

+

+ + {{ link }} + +

+

+ Si vous n'ètes pas à l'origine de cette demande, merci d'ignorer ce mail.  +

+

 

+

+ L'Équipe BeerCrackerz +

+{% endautoescape %} diff --git a/back/app/templates/email/html/user-creation.html b/back/app/templates/email/html/user-creation.html new file mode 100644 index 0000000..af84fe1 --- /dev/null +++ b/back/app/templates/email/html/user-creation.html @@ -0,0 +1,25 @@ +{% autoescape off %} +

+ + Bonjour et surtout bienvenu sur BeerCrackerz, {{ user.username | capfirst }}, + + +

+

 

+

+ Nous vous remercions de votre inscription et espérons que BeerCrackerz saura combler vos meilleures virées houblonées. Cette carte communautaire recense les meilleurs endroits pour se délecter d'une bière, mais également pour trouver de quoi se ravitailler! +

+

+ Tout ce qu'il vous reste à faire pour utiliser BeerCrackerz, c'est de suivre le lien suivant pour valider votre adresse mail (et ainsi vérifier votre compte). +

+

+ {{ link }} +

+

+ À vos, spots!
+

+

 

+

+ L'Équipe BeerCrackerz +

+{% endautoescape %} diff --git a/back/app/templates/email/text/password-reset.txt b/back/app/templates/email/text/password-reset.txt new file mode 100644 index 0000000..08955c8 --- /dev/null +++ b/back/app/templates/email/text/password-reset.txt @@ -0,0 +1,10 @@ +Bonjour {{ user.username | capfirst }}, + +Une demande de réinitialisation de votre mot de passe viens d'être effectuée. Pour changer votre mot de passe, veuillez cliquer sur le lien suivant : + +{{ link }} + +Si vous n'êtes pas à l'origine de cette demande, merci d'ignorer ce mail. + +L'Équipe BeerCrackerz + diff --git a/back/app/templates/email/text/user-creation.txt b/back/app/templates/email/text/user-creation.txt new file mode 100644 index 0000000..adebb52 --- /dev/null +++ b/back/app/templates/email/text/user-creation.txt @@ -0,0 +1,10 @@ +Bonjour et surtout bienvenu sur BeerCrackerz, {{ user.username | capfirst }}, + +Nous vous remercions de votre inscription et espérons que BeerCrackerz saura combler vos meilleures virées houblonées. Cette carte communautaire recense les meilleurs endroits pour se délecter d'une bière, mais également pour trouver de quoi se ravitailler! + +Tout ce qu'il vous reste à faire pour utiliser BeerCrackerz, c'est de suivre le lien suivant pour valider votre adresse mail (et ainsi vérifier votre compte). + +{{ link }} + +À vos, spots! +L'Équipe BeerCrackerz \ No newline at end of file diff --git a/back/app/templates/error.html b/back/app/templates/error.html new file mode 100644 index 0000000..4e48541 --- /dev/null +++ b/back/app/templates/error.html @@ -0,0 +1,77 @@ +{% load static %} + + + + + Beer Crackerz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Fatal error

+

Please contact administrator at contact@messe-basse-production.com and please provide the following text :

+

Contactez les administrateurs à contact@messe-basse-production.com en incluant le texte suivant :

+

Póngase en contacto con los administradores en contact@messe-basse-production.com incluyendo el siguiente texto :

+
+

+
+

Start BeerCrackerz again

+ + + diff --git a/back/app/templates/index.html b/back/app/templates/index.html new file mode 100644 index 0000000..bddc599 --- /dev/null +++ b/back/app/templates/index.html @@ -0,0 +1,84 @@ + +{% load static %} +{% csrf_token %} + + + + Beer Crackerz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+
+ +
+
+

+

+
+
+
+

+
+ + + + + + + + + diff --git a/back/app/templates/modal/addbar.html b/back/app/templates/modal/addbar.html new file mode 100644 index 0000000..d3b7c3a --- /dev/null +++ b/back/app/templates/modal/addbar.html @@ -0,0 +1,34 @@ +{% load static %} +
+ +

{BAR_TITLE}

+

{BAR_SUBTITLE}

+ + + +
+ + + +
+
+ + +
+ rating-star + rating-star + rating-star + rating-star + rating-star +
+
+ pricing-dollar + pricing-dollar + pricing-dollar +
+
+
+ + +
+
diff --git a/back/app/templates/modal/addshop.html b/back/app/templates/modal/addshop.html new file mode 100644 index 0000000..23ac56b --- /dev/null +++ b/back/app/templates/modal/addshop.html @@ -0,0 +1,34 @@ +{% load static %} +
+ +

{SHOP_TITLE}

+

{SHOP_SUBTITLE}

+ + + +
+ + + +
+
+ + +
+ rating-star + rating-star + rating-star + rating-star + rating-star +
+
+ pricing-dollar + pricing-dollar + pricing-dollar +
+
+
+ + +
+
diff --git a/back/app/templates/modal/addspot.html b/back/app/templates/modal/addspot.html new file mode 100644 index 0000000..e64d006 --- /dev/null +++ b/back/app/templates/modal/addspot.html @@ -0,0 +1,26 @@ +{% load static %} +
+ +

{SPOT_TITLE}

+

{SPOT_SUBTITLE}

+ + + +
+ + + +
+ +
+ rating-star + rating-star + rating-star + rating-star + rating-star +
+
+ + +
+
diff --git a/back/app/templates/modal/deletemark.html b/back/app/templates/modal/deletemark.html new file mode 100644 index 0000000..b22fa2f --- /dev/null +++ b/back/app/templates/modal/deletemark.html @@ -0,0 +1,9 @@ +
+ +

{MODAL_TITLE}

+

{MODAL_DESC}

+
+ + +
+
diff --git a/back/app/templates/modal/editbar.html b/back/app/templates/modal/editbar.html new file mode 100644 index 0000000..5d2f72b --- /dev/null +++ b/back/app/templates/modal/editbar.html @@ -0,0 +1,34 @@ +{% load static %} +
+ +

{BAR_TITLE}

+

{BAR_SUBTITLE}

+ + + +
+ + + +
+
+ + +
+ rating-star + rating-star + rating-star + rating-star + rating-star +
+
+ pricing-dollar + pricing-dollar + pricing-dollar +
+
+
+ + +
+
diff --git a/back/app/templates/modal/editshop.html b/back/app/templates/modal/editshop.html new file mode 100644 index 0000000..604a28f --- /dev/null +++ b/back/app/templates/modal/editshop.html @@ -0,0 +1,34 @@ +{% load static %} +
+ +

{SHOP_TITLE}

+

{SHOP_SUBTITLE}

+ + + +
+ + + +
+
+ + +
+ rating-star + rating-star + rating-star + rating-star + rating-star +
+
+ pricing-dollar + pricing-dollar + pricing-dollar +
+
+
+ + +
+
diff --git a/back/app/templates/modal/editspot.html b/back/app/templates/modal/editspot.html new file mode 100644 index 0000000..5b8cca1 --- /dev/null +++ b/back/app/templates/modal/editspot.html @@ -0,0 +1,26 @@ +{% load static %} +
+ +

{SPOT_TITLE}

+

{SPOT_SUBTITLE}

+ + + +
+ + + +
+ +
+ rating-star + rating-star + rating-star + rating-star + rating-star +
+
+ + +
+
diff --git a/back/app/templates/modal/hideshow.html b/back/app/templates/modal/hideshow.html new file mode 100644 index 0000000..2c2839d --- /dev/null +++ b/back/app/templates/modal/hideshow.html @@ -0,0 +1,29 @@ +{% load static %} +
+ +

{MODAL_TITLE}

+
+
+
+ show-spots + + +
+
+ show-shops + + +
+
+ show-bars + + +
+
+
+

{HELPER_LABEL}

+

+
+
+ +
diff --git a/back/app/templates/modal/startuphelp.html b/back/app/templates/modal/startuphelp.html new file mode 100644 index 0000000..9cc7ce2 --- /dev/null +++ b/back/app/templates/modal/startuphelp.html @@ -0,0 +1,49 @@ +{% load static %} +
+ +

{MODAL_TITLE}

+
+ + +
+

{MODAL_PAGE_1_1}

+ user-circles +

{MODAL_PAGE_1_2}

+
+
+

{MODAL_PAGE_2_1}

+ spot-mark +

{MODAL_PAGE_2_2}

+
+
+

{MODAL_PAGE_3_1}

+ shop-mark +

{MODAL_PAGE_3_2}

+
+
+

{MODAL_PAGE_4_1}

+ bar-mark +

{MODAL_PAGE_4_2}

+
+
+

{MODAL_PAGE_5_1}

+ commands +

{MODAL_PAGE_5_2}

+
+
+
+
+
+
+
+
+
+
+ + +
+
diff --git a/back/app/templates/modal/updatepp.html b/back/app/templates/modal/updatepp.html new file mode 100644 index 0000000..f2ddb15 --- /dev/null +++ b/back/app/templates/modal/updatepp.html @@ -0,0 +1,12 @@ +
+ +

{MODAL_TITLE}

+

{UPDATE_PP_DESC}

+
+ uploaded-pp +
+
+ + +
+
diff --git a/back/app/templates/modal/user.html b/back/app/templates/modal/user.html new file mode 100644 index 0000000..80545a4 --- /dev/null +++ b/back/app/templates/modal/user.html @@ -0,0 +1,52 @@ +{% load static %} + diff --git a/back/app/templates/popup/bar.html b/back/app/templates/popup/bar.html new file mode 100644 index 0000000..79d86b5 --- /dev/null +++ b/back/app/templates/popup/bar.html @@ -0,0 +1,28 @@ +{% load static %} + diff --git a/back/app/templates/popup/shop.html b/back/app/templates/popup/shop.html new file mode 100644 index 0000000..cc5cdda --- /dev/null +++ b/back/app/templates/popup/shop.html @@ -0,0 +1,28 @@ +{% load static %} + diff --git a/back/app/templates/popup/spot.html b/back/app/templates/popup/spot.html new file mode 100644 index 0000000..7cfd8ab --- /dev/null +++ b/back/app/templates/popup/spot.html @@ -0,0 +1,28 @@ +{% load static %} + diff --git a/back/app/templates/welcome.html b/back/app/templates/welcome.html new file mode 100644 index 0000000..63c02ff --- /dev/null +++ b/back/app/templates/welcome.html @@ -0,0 +1,99 @@ + +{% load static %} +{% csrf_token %} + + + + Login | BeerCrackerz + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+

+

+
+
+
+

+
+
+ + + + + + + + + + diff --git a/back/app/tests.py b/back/app/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/back/app/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/back/app/urls.py b/back/app/urls.py new file mode 100644 index 0000000..92b20bc --- /dev/null +++ b/back/app/urls.py @@ -0,0 +1,30 @@ +from django.urls import path + +from .views import IndexView, WelcomeView, ErrorView, LoginAsideView, RegisterAsideView, ForgotPasswordAsideView, ResetPasswordAsideView, SpotPopupView, ShopPopupView, BarPopupView, DeleteMarkModalView, EditBarModalView, EditShopModalView, EditSpotModalView, HideShowModalView, NewBarModalView, NewShopModalView, NewSpotModalView, UserModalView, UpdatePPModalView, StartupHelpModalView + +urlpatterns = [ + path('', IndexView.as_view(), name='index'), + path('welcome/', WelcomeView.as_view(), name='welcome'), + path('error/', ErrorView.as_view(), name='error'), + # Aside template urls for Auth features + path('aside/login', LoginAsideView.as_view(), name='loginAside'), + path('aside/register', RegisterAsideView.as_view(), name='registerAside'), + path('aside/forgotpassword', ForgotPasswordAsideView.as_view(), name='forgotPasswordAside'), + path('aside/resetpassword', ResetPasswordAsideView.as_view(), name='resetPasswordAside'), + # Markers popup template urls + path('popup/spot', SpotPopupView.as_view(), name='spotPopup'), + path('popup/shop', ShopPopupView.as_view(), name='shopPopup'), + path('popup/bar', BarPopupView.as_view(), name='barPopup'), + # App modal template urls + path('modal/deletemark', DeleteMarkModalView.as_view(), name='deleteMarkModal'), + path('modal/editbar', EditBarModalView.as_view(), name='editBarModal'), + path('modal/editshop', EditShopModalView.as_view(), name='editShopModal'), + path('modal/editspot', EditSpotModalView.as_view(), name='editSpotModal'), + path('modal/hideshow', HideShowModalView.as_view(), name='hideShowModal'), + path('modal/addbar', NewBarModalView.as_view(), name='newBarModal'), + path('modal/addshop', NewShopModalView.as_view(), name='newShopModal'), + path('modal/addspot', NewSpotModalView.as_view(), name='newSpotModal'), + path('modal/user', UserModalView.as_view(), name='userModal'), + path('modal/updatepp', UpdatePPModalView.as_view(), name='updatePPModal'), + path('modal/startuphelp', StartupHelpModalView.as_view(), name='startupHelpModal') +] diff --git a/back/app/utils.py b/back/app/utils.py new file mode 100644 index 0000000..2521b85 --- /dev/null +++ b/back/app/utils.py @@ -0,0 +1,5 @@ +def get_or_raise_error(model, error, **kwargs): + try: + return model.objects.get(**kwargs) + except model.DoesNotExist: + raise error diff --git a/back/app/utils/__init__.py b/back/app/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/app/utils/token.py b/back/app/utils/token.py new file mode 100644 index 0000000..3ac0e10 --- /dev/null +++ b/back/app/utils/token.py @@ -0,0 +1,19 @@ +from django.contrib.auth.tokens import default_token_generator +from django.utils.encoding import force_bytes, force_str +from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode + + +def get_token_from_user(user): + return default_token_generator.make_token(user) + + +def check_token(user, token): + return default_token_generator.check_token(user, token) + + +def encode_uid(uid): + return urlsafe_base64_encode(force_bytes(uid)) + + +def decode_uid(uidb64): + return force_str(urlsafe_base64_decode(uidb64)) diff --git a/back/app/views.py b/back/app/views.py new file mode 100644 index 0000000..106ee45 --- /dev/null +++ b/back/app/views.py @@ -0,0 +1,99 @@ +from django.contrib.auth.mixins import LoginRequiredMixin +from django.shortcuts import redirect +from django.views.generic import TemplateView + + +# Whole pages views (index and welcome) +class IndexView(LoginRequiredMixin, TemplateView): + template_name = 'index.html' + login_url = '/welcome' + redirect_field_name = 'next' + + +class WelcomeView(TemplateView): + template_name = 'welcome.html' + + # Redirect to index view if the user is already logged in. + def dispatch(self, request, *args, **kwargs): + if request.user.is_authenticated: + return redirect('index') + return super().dispatch(request, *args, **kwargs) + + +class ErrorView(TemplateView): + template_name = 'error.html' + + +# Aside views for Auth features +class LoginAsideView(TemplateView): + template_name = 'aside/login.html' + + +class RegisterAsideView(TemplateView): + template_name = 'aside/register.html' + + +class ForgotPasswordAsideView(TemplateView): + template_name = 'aside/forgot-password.html' + + +class ResetPasswordAsideView(TemplateView): + template_name = 'aside/reset-password.html' + + +# Popup views for markers +class SpotPopupView(TemplateView): + template_name = 'popup/spot.html' + + +class ShopPopupView(TemplateView): + template_name = 'popup/shop.html' + + +class BarPopupView(TemplateView): + template_name = 'popup/bar.html' + + +# Modal views +class DeleteMarkModalView(TemplateView): + template_name = 'modal/deletemark.html' + + +class EditBarModalView(TemplateView): + template_name = 'modal/editbar.html' + + +class EditShopModalView(TemplateView): + template_name = 'modal/editshop.html' + + +class EditSpotModalView(TemplateView): + template_name = 'modal/editspot.html' + + +class HideShowModalView(TemplateView): + template_name = 'modal/hideshow.html' + + +class StartupHelpModalView(TemplateView): + template_name = 'modal/startuphelp.html' + + +class NewBarModalView(TemplateView): + template_name = 'modal/addbar.html' + + +class NewShopModalView(TemplateView): + template_name = 'modal/addshop.html' + + +class NewSpotModalView(TemplateView): + template_name = 'modal/addspot.html' + + +class UserModalView(TemplateView): + template_name = 'modal/user.html' + + +class UpdatePPModalView(TemplateView): + template_name = 'modal/updatepp.html' diff --git a/back/back/__init__.py b/back/back/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/back/back/asgi.py b/back/back/asgi.py new file mode 100644 index 0000000..ef1c359 --- /dev/null +++ b/back/back/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for back project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'back.settings') + +application = get_asgi_application() diff --git a/back/back/settings.py b/back/back/settings.py new file mode 100644 index 0000000..5ce3ecd --- /dev/null +++ b/back/back/settings.py @@ -0,0 +1,163 @@ +""" +Django settings for back project. + +Generated by 'django-admin startproject' using Django 4.1. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.1/ref/settings/ +""" + +import os +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.environ.get('SECRET_KEY', 'secret') + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = bool(int(os.environ.get('DEBUG', 0))) + +ALLOWED_HOSTS = [] +ALLOWED_HOSTS.extend(filter(None, os.environ.get('ALLOWED_HOSTS', '').split(';'))) + +CSRF_TRUSTED_ORIGINS = [] +CSRF_TRUSTED_ORIGINS.extend(filter(None, os.environ.get('CSRF_TRUSTED_ORIGINS', '').split(';'))) + +SERVER_URL = os.environ.get('SERVER_URL') + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'django_extensions', + 'rest_framework', + 'django_filters', + 'corsheaders', + 'app', + 'api', +] + +MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'back.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'back.wsgi.application' + +# Database +# https://docs.djangoproject.com/en/4.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.postgresql', + 'HOST': os.environ.get('DB_HOST'), + 'PORT': os.environ.get('DB_PORT'), + 'NAME': os.environ.get('DB_NAME'), + 'USER': os.environ.get('DB_USER'), + 'PASSWORD': os.environ.get('DB_PASSWORD'), + } +} + +# Default primary key field type +# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Password validation +# https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +AUTH_USER_MODEL = "app.User" + +REST_FRAMEWORK = { + 'DEFAULT_AUTHENTICATION_CLASSES': ['rest_framework.authentication.SessionAuthentication'], + 'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticatedOrReadOnly'], + 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'], +} + +DEFAULT_FROM_EMAIL = '"BeerCrackerz" ' + +if bool(int(os.environ.get('BACKEND_USE_EMAIL_FILE_SYSTEM', 0))): + EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' + EMAIL_FILE_PATH = os.path.join(BASE_DIR, 'tmp/email') +else: + EMAIL_BACKEND = 'django_mailjet.backends.MailjetBackend' + MAILJET_API_KEY = os.environ.get('MAILJET_API_KEY') + MAILJET_API_SECRET = os.environ.get('MAILJET_API_SECRET') + +# Internationalization +# https://docs.djangoproject.com/en/4.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'Europe/Paris' + +USE_I18N = False + +USE_L10N = True + +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.1/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = '/vol/static' + +STATICFILES_DIRS = [ + '/back/static' +] + +MEDIA_URL = '/media/' +MEDIA_ROOT = '/vol/media' diff --git a/back/back/urls.py b/back/back/urls.py new file mode 100644 index 0000000..c194655 --- /dev/null +++ b/back/back/urls.py @@ -0,0 +1,23 @@ +"""back URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('', include('app.urls')), + path('api/', include('api.urls')), + path('admin/', admin.site.urls), +] diff --git a/back/back/wsgi.py b/back/back/wsgi.py new file mode 100644 index 0000000..7a52296 --- /dev/null +++ b/back/back/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for back project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'back.settings') + +application = get_wsgi_application() diff --git a/back/manage.py b/back/manage.py new file mode 100755 index 0000000..600455f --- /dev/null +++ b/back/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'back.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/back/requirements.txt b/back/requirements.txt new file mode 100644 index 0000000..292e42b --- /dev/null +++ b/back/requirements.txt @@ -0,0 +1,11 @@ +Django==4.1.2 +django-cors-headers==3.13.0 +djangorestframework==3.14.0 +django-multiselectfield +pytz==2022.5 +django-filter==22.1 +django_extensions==3.2.1 +django-mailjet +Pillow==9.2.0 +psycopg2>=2.8.6,<2.8.7 +uWSGI>=2.0.19.1,<2.1 \ No newline at end of file diff --git a/bc.sh b/bc.sh new file mode 100755 index 0000000..66fabbd --- /dev/null +++ b/bc.sh @@ -0,0 +1,432 @@ +#!/usr/bin/env bash +vers="0.1.0" # Must match package.json and BeerCrackerz.js version number +basedir=$(dirname "${0}") +unset backsecretkey +unset dbuser +unset dbpassword +unset serverurl +unset mailjetapi +unset mailjetsecret + +# Method to display Command help and usage + +function usage(){ + echo -e "bc.sh ${1} : Command help\n" + echo -e "Usage : ./bc.sh [command] [argument]\n" + echo -e " -i, --install [\e[33mdev\e[39m/\e[33mprod\e[39m] – \e[33mMandatory\e[39m" + echo -e " Configure environment file and install BeerCrackerz on the system\n" + echo -e " -e, --edit [\e[33mdev\e[39m/\e[33mprod\e[39m] – \e[33mMandatory\e[39m" + echo -e " Edit the environment file\n" + echo -e " -b, --build [\e[33mdev\e[39m/\e[33mprod\e[39m] – \e[33mMandatory\e[39m" + echo -e " Build the docker containers\n" + echo -e " -s, --start [\e[33mdev\e[39m/\e[33mprod\e[39m] – \e[33mMandatory\e[39m" + echo -e " Start BeerCrackerz application\n" + echo -e " -u, --update [\e[33mdev\e[39m/\e[33mprod\e[39m] – \e[33mMandatory\e[39m" + echo -e " Quit, pull, build and start aplication\n" + echo -e " -q, --quit [\e[33mdev\e[39m/\e[33mprod\e[39m] – \e[33mMandatory\e[39m" + echo -e " Stop any running BeerCrackerz application\n" + echo -e " -r, --reset [\e[32mhard\e[39m] – \e[32mOptional\e[39m" + echo -e " Remove existing database and docker images, hard argument will docker prune" + echo -e " This command will not remove any .env configuration files\n" + echo -e " -g, --gource [\e[32msave\e[39m] – \e[32mOptional\e[39m" + echo -e " Review git history using gource, save argument to save output as mp4 in ./\n" + echo -e " -a, --about Information about BeerCrackerz project" + echo -e " -h, --help Display command usage\n" +} + +# Method to check if given command is installed on the system +isInstalled() { + command -v "${1}" >/dev/null 2>&1 + if [[ $? -ne 0 ]]; then + echo -e "\e[31mERROR\e[39m ${1} is not installed on the system" + echo -e " Ensure docker, docker-compose and npm are installed" + echo -e " On a production environment, nginx must be installed as well" + echo -e " -> https://github.com/MesseBasseProduction/BeerCrackerz/wiki" + exit 0 + fi +} + +# User .env variables inputs +updateVariables() { + while [ -z ${backsecretkey} ]; do + read -rp " 1/7. The backend secret key : " backsecretkey + done + # Database user + while [ -z ${dbuser} ]; do + read -rp " 2/7. The database username : " dbuser + done + # Database password (not empty and >10 characters) + while [[ ${dbpassword} = "" || ${#dbpassword} -lt 10 ]]; do + read -rp " 3/7. The database password (> 10 characters) : " dbpassword + done + # Server url with protocol + while [ -z ${serverurl} ]; do + read -rp " 5/7. The production server url: " serverurl + done + # Mailjet API key + while [ -z ${mailjetapi} ]; do + read -rp " 6/7. The MailJet API key : " mailjetapi + done + # Mailjet API secret + while [ -z ${mailjetsecret} ]; do + read -rp " 7/7. The MailJet API secret : " mailjetsecret + done +} + +# development .env file creation method +devInstall() { + touch "${basedir}"/.conf/development/conf.env + { echo "# NGINX" + echo "NGINX_NAME=beer_crackerz_nginx" + echo "SERVER_HOST=localhost" + echo "SERVER_HTTP_PORT=8080" + echo "" + echo "# DATABASE" + echo "DB_POSTGRES_VERSION=14.2-alpine" + echo "DB_HOST=beer_crackerz_db" + echo "DB_PORT=5432" + echo "DB_NAME=beer_crackerz" + echo "DB_USER=${2}" + echo "DB_PASSWORD=${3}" + echo "" + echo "# ADMINER" + echo "DB_ADMINER_NAME=beer_crackerz_adminer" + echo "DB_ADMINER_PORT=8081" + echo "DB_ADMINER_CONTAINER_PORT=8080" + echo "" + echo "# BACKEND" + echo "BACKEND_NAME=beer_crackerz_back" + echo "BACKEND_PORT=8000" + echo "BACKEND_DEBUG=1" + echo "BACKEND_ALLOWED_HOSTS=*" + echo "BACKEND_USE_EMAIL_FILE_SYSTEM=1" + echo "BACKEND_SECRET_KEY=${1}" + echo "CSRF_TRUSTED_ORIGINS=http://localhost:8080;http://127.0.0.1:8080" + echo "" + echo "# MAILJET" + echo "MAILJET_API_KEY=${4}" + echo "MAILJET_API_SECRET=${5}" + } >> "${basedir}"/.conf/development/conf.env +} + +# production .env file creation method +prodInstall() { + touch "${basedir}"/.conf/production/conf.env + { echo "# NGINX" + echo "NGINX_NAME=beer_crackerz_nginx" + echo "SERVER_HOST=127.0.0.1" + echo "SERVER_PORT=8000" + echo "SERVER_URL=${4}" + echo "" + echo "# DATABASE" + echo "DB_POSTGRES_VERSION=14.2-alpine" + echo "DB_HOST=beer_crackerz_db" + echo "DB_PORT=5432" + echo "DB_NAME=beer_crackerz" + echo "DB_USER=${2}" + echo "DB_PASSWORD=${3}" + echo "" + echo "# BACKEND" + echo "BACKEND_NAME=beer_crackerz_back" + echo "BACKEND_PORT=8000" + echo "BACKEND_DEBUG=0" + echo "BACKEND_ALLOWED_HOSTS=127.0.0.1" + echo "BACKEND_USE_EMAIL_FILE_SYSTEM=0" + echo "BACKEND_SECRET_KEY=${1}" + echo "CSRF_TRUSTED_ORIGINS=${4}" + echo "" + echo "# MAILJET" + echo "MAILJET_API_KEY=${5}" + echo "MAILJET_API_SECRET=${6}" + } >> "${basedir}"/.conf/production/conf.env +} + +function createConfFile() { + # Initialization sequence, fill .env file to fit user inputs and build docker images in either dev, prod or local prod mode + # Check if all dependencies are installed before doing anything + for COMMAND in "docker" "docker-compose" "npm"; do + isInstalled "${COMMAND}" + done + + # # Check for previous existing .env files, ensure user want to override existing configuration + if [[ -f "${basedir}"/.conf/development/conf.env || -f "${basedir}"/.conf/production/conf.env ]]; then + echo -e "\e[93mWARNING\e[39m BeerCrackerz has at least one configuration file which might be overriden" + # Can't init to blank to get in while read loop + replaceconf="bc" + # Wait for user to send yY/nN or blank + while [[ "${replaceconf}" != "" && "${replaceconf}" != "y" && "${replaceconf}" != "Y" && "${replaceconf}" != "n" && "${replaceconf}" != "N" ]]; do + read -rp " Do you still want to proceed? [y/N] " replaceconf + done + # Exit if user didn't enter anything, or entered n/N + if [ "${replaceconf}" = "" ] || [ "${replaceconf}" = "n" ] || [ "${replaceconf}" = "N" ]; then + exit 0 + fi + fi + # Welcome message + echo -e "Welcome to the BeerCrackerz installation wizard!" + echo -e "Before going further, ensure you've read the installation entry of the wiki before going any further" + echo -e "-> https://github.com/MesseBasseProduction/BeerCrackerz/wiki\n" + echo -e "Please fill the following information to properly configure BeerCrackerz :\n" + # Request info from user + updateVariables + # Runtime mode to configure + if [ "${1}" = "dev" ]; then + rm -rf "${basedir}"/.conf/development/conf.env + echo "Creating configuration file for development environment." + devInstall "${backsecretkey}" "${dbuser}" "${dbpassword}" "${mailjetapi}" "${mailjetsecret}" + elif [ "${1}" = "prod" ]; then + rm -rf "${basedir}"/.conf/production/conf.env + echo "Creating configuration file for production environment." + prodInstall "${backsecretkey}" "${dbuser}" "${dbpassword}" "${serverurl}" "${mailjetapi}" "${mailjetsecret}" + fi + echo # Line break + echo -e "\e[32mSUCCESS\e[39m BeerCrackerz installed!" + +} + +function editConfFile() { + # Runtime mode to configure + if [ ${1} == "prod" ]; then + confFile=$(echo $(pwd)/.conf/production/conf.env) + else + confFile=$(echo $(pwd)/.conf/development/conf.env) + fi + echo -e "You are going to modify \033[38;5;226m${confFile}\033[00m" + # Looping over all terms that will need to be updated in file + for envVar in "DB_USER" "DB_PASSWORD" "BACKEND_SECRET_KEY" "MAILJET_API_KEY" "MAILJET_API_SECRET" "CSRF_TRUSTED_ORIGINS" "SERVER_URL"; do + # Get whole line matching current envVar which need an update + tmp=$(grep ${envVar} ${confFile}) + # Check if current envVar exists in file + # if yes, then update it or not + if [ $? -eq 0 ]; then + # Printing current value in file + echo "Currently ${tmp}" + # Can't start looping with an empty variable + replaceVar="bc" + while [[ ${replaceVar} != "" && ${replaceVar} != "y" && "${replaceVar}" != "Y" && "${replaceVar}" != "n" && "${replaceVar}" != "N" ]]; do + read -rp " Do you want to replace ${envVar} current value ? [y/n] " replaceVar + done + # If var needs to be replaced then replace it + # Else continue to next var + if [[ ${replaceVar} == "y" || ${replaceVar} == "Y" ]]; then + read -rp " ${envVar} = " replaceVar + sed -i -e "s/${tmp}/${envVar}=${replaceVar}/g" ${confFile} + fi + fi + done + + echo -e "\e[32mSUCCESS\e[39m BeerCrackerz edited!" +} + +function buildApp(){ + if [ "${1}" = "dev" ]; then + echo -e "Building BeerCrackerz for development environment" + eval "npm run build" + eval "docker-compose --file ${basedir}/docker-compose.yml --env-file ${basedir}/.conf/development/conf.env build" + elif [ "${1}" = "prod" ]; then + echo -e "Building BeerCrackerz for production environment" + eval "npm run build" + eval "docker-compose --file ${basedir}/docker-compose.prod.yml --env-file ${basedir}/.conf/production/conf.env build" + fi + + echo -e "\n\e[32mSUCCESS\e[39m BeerCrackerz built!" +} + +function startApp(){ + if [ "${1}" = "dev" ]; then + echo -e "Starting BeerCrackerz in development environment" + eval "docker-compose --file ${basedir}/docker-compose.yml --env-file ${basedir}/.conf/development/conf.env up -d" + elif [ "${1}" = "prod" ]; then + echo -e "Starting BeerCrackerz in production environment" + eval "docker-compose --file ${basedir}/docker-compose.prod.yml --env-file ${basedir}/.conf/production/conf.env up -d" + fi + + echo -e "\n\e[32mSUCCESS\e[39m BeerCrackerz started!" + echo -e " If this is the first start, please run the following command when the app is started :" + echo -e " $ docker exec -it beer_crackerz_back python manage.py createsuperuser" +} + +function quitApp(){ + if [ "${1}" = "dev" ]; then + echo -e "Stoping BeerCrackerz containers in development environment" + eval "docker-compose --file ${basedir}/docker-compose.yml --env-file ${basedir}/.conf/development/conf.env down" + elif [ "${1}" = "prod" ]; then + echo -e "Stoping BeerCrackerz containers in production environment" + eval "docker-compose --file ${basedir}/docker-compose.prod.yml --env-file ${basedir}/.conf/production/conf.env down" + fi + + echo -e "\n\e[32mSUCCESS\e[39m BeerCrackerz exited!" +} + +function resetApp(){ + # Reset BeerCrackerz, clear database files and docker images + # Warn user that the command will remove database and images + echo -e "\e[93mWARNING\e[39m This command will erase any existing BeerCrackerz' docker images" + resetBc="bc" # Can't init to blank to get in while read loop + # Wait for user to send yY/nN or blank + while [[ "${resetBc}" != "" && "${resetBc}" != "y" && "${resetBc}" != "Y" && "${resetBc}" != "n" && "${resetBc}" != "N" ]]; do + read -rp " Do you want to fully reset BeerCrackerz? [y/N] " resetBc + done + # Exit if user didn't enter anything, or entered n/N + if [ "${resetBc}" = "" ] || [ "${resetBc}" = "n" ] || [ "${resetBc}" = "N" ]; then + exit 0 + fi + # Ensure all docker are stopped + echo # Line break + echo -e "1/3. Stopping any BeerCrackerz containers" + eval "docker-compose stop" + echo # Line break + # Remove BeerCrackerz's related dockers + echo -e "2/3. Removing BeerCrackerz containers" + eval "docker-compose --file ${basedir}/docker-compose.yml --env-file ${basedir}/.conf/development/conf.env down --rmi all --remove-orphans" + eval "docker-compose --file ${basedir}/docker-compose.prod.yml --env-file ${basedir}/.conf/production/conf.env down --rmi all --remove-orphans" + echo # Line break + # Reset hard argument + if [ "${1}" = "hard" ]; then + echo -e "3/3. Remove all existing docker images\n" + eval "docker system prune" + echo -e "\n\e[32mSUCCESS\e[39m BeerCrackerz reset!" + # Rebuild the whole thing otherwise + else + echo -e "3/3. Complete BeerCrackerz reset" + echo -e "\n\e[32mSUCCESS\e[39m BeerCrackerz reset!" + fi +} + +function gourceControl(){ + # Check if gource is installed on the system + isInstalled "gource" + # Start gource with custom parameters + gourceOptions="--fullscreen --multi-sampling --auto-skip-seconds 0.1 --seconds-per-day 0.15 --elasticity 0.02 \ + --camera-mode overview --font-size 18 --stop-at-end --bloom-intensity 0.5 --date-format '%d %B %Y' --hide mouse,progress \ + --title 'BeerCrackerz - version ${vers}, Messe Basse Production 2022/2023' --logo ${basedir}/static/img/logo-tiny.png --user-image-dir ${basedir}/static/img/about" + ffmpegOptions="--output-ppm-stream - | ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i - -vcodec libx264 -preset medium -crf 1 -threads 0 -bf 0 ${basedir}/bc-git-history.mp4" + if [ "$1" = 'save' ]; then + isInstalled "ffmpeg" + echo -e "Exporting gource visualization as a mp4 file" + eval "gource $gourceOptions $ffmpegOptions" + fi +} + +function updateApp(){ + echo -e "Start updating BeerCrackerz!" + quitApp ${1} + buildApp ${1} + startApp ${1} + + echo -e "\e[32mSUCCESS\e[39m BeerCrackerz updated!" +} + +# Script header +echo # Line break +echo -e " ## ---------------------------------- ##" +echo -e " ## \e[92mBeerCrackerz\e[39m ##" +echo -e " ## 2022/2023 -- GPL-3.0 ##" +echo -e " ## v${vers} ##" +echo -e " ## ---------------------------------- ##" +echo # Line break + +# First of all, test if user has send an argument +if [ $# -eq 0 ]; then + echo -e "bc.sh : Missing argument\n" + echo -e "\e[31mERROR\e[39m Command executed without any arguments" + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 +fi + +for option in ${@} +do + case "${1}" in + -h|h|--help|help) + usage + exit 0 + ;; + -i|i|--install|install) + if [[ ! ${2} == @(dev|prod) ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to create BeerCrackerz configuration file." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + createConfFile ${2} + shift + ;; + -e|e|--edit|edit) + if [[ ! ${2} == @(dev|prod) ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to edit BeerCrackerz configuration file." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + editConfFile ${2} + shift + ;; + -b|b|--build|build) + if [[ ! ${2} == @(dev|prod) ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to build BeerCrackerz." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + buildApp ${2} + shift + ;; + -s|s|--start|start) + if [[ ! ${2} == @(dev|prod) ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to start BeerCrackerz." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + startApp ${2} + shift + ;; + -u|u|--update|update) + if [[ ! ${2} == @(dev|prod) ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to update BeerCrackerz." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + updateApp ${2} + exit 0 + ;; + -q|q|--quit|quit) + if [[ ! ${2} == @(dev|prod) ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to stop BeerCrackerz." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + quitApp ${2} + shift + ;; + -r|r|--reset|reset) + if [[ ${2} =~ ^\- ]]; then + resetApp + elif [[ ! ${2} == @("hard"|"") ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to reset BeerCrackerz." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + else + resetApp ${2} + shift + fi + ;; + -g|g|--gource|gource) + if [[ ! ${2} == @("save"|"") ]]; then + echo -e "\e[31mERROR\e[39m \"${2}\" is not a supported argument to use with gource." + echo -e " Check command help for available arguments: ./bc.sh --help" + exit 1 + fi + gourceControl ${2} + shift + ;; + -a|a|--about|about) + echo -e "bc.sh ${1} : About BeerCrackerz\nWelcome, fellow beer lovers.\nBeerCrackerz is a community web app to list the best spots to drink a fresh one while you're outside.\nIt provides a well-known map interface so it is really easy to browse, find or add unique spots!" + exit 0 + ;; + "") + exit 0 + ;; + *) + echo -e "\e[31mERROR\e[39m Invalid '${1}' option. See ${0} --help" + exit 1 + ;; + esac + shift +done diff --git a/doc/beercrackerz/0.0.1/BeerCrackerz.js.html b/doc/beercrackerz/0.0.1/BeerCrackerz.js.html deleted file mode 100644 index f1eefe0..0000000 --- a/doc/beercrackerz/0.0.1/BeerCrackerz.js.html +++ /dev/null @@ -1,1242 +0,0 @@ - - - - - JSDoc: Source: BeerCrackerz.js - - - - - - - - - - -
- -

Source: BeerCrackerz.js

- - - - - - -
-
-
import './BeerCrackerz.scss';
-import MapHelper from './js/MapHelper.js';
-import ZoomSlider from './js/ui/ZoomSlider.js';
-import LangManager from './js/utils/LangManager.js';
-import Notification from './js/ui/Notification.js';
-import Rating from './js/ui/Rating.js';
-import Utils from './js/utils/Utils.js';
-
-
-/**
- * @class
- * @constructor
- * @public
- * @extends MapHelper
-**/
-class BeerCrackerz extends MapHelper {
-
-
-	/**
-	 * @summary The BeerCrackerz main component
-	 * @author Arthur Beaulieu
-	 * @since January 2022
-	 * @description
-	 * <blockquote>
-	 * This component handles the whole BeerCrackerz app. It includes the map manipulation,
-	 * the geolocation API to update the user position and process any map events that are
-	 * relevant to an UX stand point. For more information, please consult the application
-	 * description page at <a href="https://about.beercrackerz.org">https://about.beercrackerz.org/</a>
-	 * </blockquote>
-	 **/
-	constructor() {
-    super();
-    /**
-     * The core Leaflet.js map
-     * @type {Object}
-     * @private
-     **/
-    this._map = null;
-    /**
-     * The zoom slider handler
-     * @type {Object}
-     * @private
-     **/
-    this._zoomSlider = null;
-    /**
-     * The notification handler
-     * @type {Object}
-     * @private
-     **/
-    this._notification = null;
-    /**
-     * The user object holds everything useful to ensure a proper session
-     * @type {Object}
-     * @private
-     **/
-    this._user = {
-      lat: 48.853121540141096, // Default lat to Paris Notre-Dame latitude
-      lng: 2.3498955769881156, // Default lng to Paris Notre-Dame longitude
-      accuracy: 0, // Accuracy in meter given by geolocation API
-      marker: null, // The user marker on map
-      circle: null, // The accuracy circle around the user marker
-      range: null, // The range in which user can add a new marker
-      color: Utils.USER_COLOR, // The color to use for circle (match the user marker color)
-			id: -1,
-			username: ''
-    };
-    /**
-     * The stored marks for spots, stores and bars
-     * @type {Object}
-     * @private
-     **/
-    this._marks = {
-      spot: [],
-      store: [],
-      bar: [],
-    };
-    /**
-     * The stored clusters for markers, see Leaflet.markercluster plugin
-     * @type {Object}
-     * @private
-     **/
-    this._clusters = {
-      spot: {},
-      store: {},
-      bar: {},
-    };
-    /**
-     * The temporary marker for new marks only
-     * @type {Object}
-     * @private
-     **/
-    this._newMarker = null;
-    /**
-     * The debug DOM object
-     * @type {Object}
-     * @private
-     **/
-    this._debugElement = null;
-    /**
-     * ID for geolocation watch callback
-     * @type {Number}
-     * @private
-     **/
-    this._watchId = null;
-    /**
-     * Flag to know if a zoom action is occuring on map
-     * @type {Boolean}
-     * @private
-     **/
-    this._isZooming = false;
-    /**
-     * The LangManager must be instantiated to handle nls accross the app
-     * @type {Boolean}
-     * @private
-     **/
-    // The BeerCrackerz app is only initialized once nls are set up
-    this._lang = new LangManager(
-      window.navigator.language.substring(0, 2),
-      this._init.bind(this)
-    );
-  }
-
-
-  // ======================================================================== //
-  // ----------------- Application initialization sequence ------------------ //
-  // ======================================================================== //
-
-
-	/**
-	 * @method
-   * @name _init
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _init() method is designed to properly configure the user session, according
-	 * to its saved preferences and its position. It first build the debug interface,
-	 * then loads the user preferences, then create the map and finally, events are listened.
-	 * </blockquote>
-	 **/
-	_init() {
-		this._debugElement = Utils.initDebugInterface();
-		this._notification = new Notification();
-    this._initUser()
-      .then(this._initPreferences.bind(this))
-      .then(this._initGeolocation.bind(this))
-      .then(this._initMap.bind(this))
-      .then(this._initEvents.bind(this))
-      .then(this._initMarkers.bind(this));
-	}
-
-
-	/**
-	 * @method
-   * @name _initUser
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _init() method initialize the user object according to its information
-	 * and statistic so the UI can be properly built.
-	 * </blockquote>
-	 * @returns {Promise} A Promise resolved when preferences are set
-	 **/
-	_initUser() {
-		return new Promise(resolve => {
-			// TODO fill user information from server
-			this._user.id = 42;
-			this._user.username = 'messmaker';
-			resolve();
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name _initPreferences
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _initPreferences() will initialize user preference if they are not set yet,
-	 * it will also update the UI according to user preferences ; debug DOM visible,
-	 * update the command classList for selected ones.
-	 * </blockquote>
-	 * @returns {Promise} A Promise resolved when preferences are set
-	 **/
-	_initPreferences() {
-		return new Promise(resolve => {
-			if (Utils.getPreference('poi-show-spot') === null) {
-				Utils.setPreference('poi-show-spot', true);
-			}
-
-			if (Utils.getPreference('poi-show-store') === null) {
-        Utils.setPreference('poi-show-store', true);
-      }
-
-			if (Utils.getPreference('poi-show-bar') === null) {
-        Utils.setPreference('poi-show-bar', true);
-      }
-
-			if (Utils.getPreference('map-plan-layer') === null) {
-        Utils.setPreference('map-plan-layer', true);
-      }
-
-			if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) {
-				window.DEBUG = true; // Ensure to set global flag if preference comes from local storage
-				Utils.setPreference('app-debug', true); // Ensure to set local storage preference if debug flag was added to the url
-				this.addDebugUI();
-      }
-			// Update icon class if center on preference is set to true
-			if (Utils.getPreference('map-center-on-user') === 'true') {
-        document.getElementById('center-on').classList.add('lock-center-on');
-      }
-
-			resolve();
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name _initGeolocation
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _initGeolocation() method will request from browser the location authorization.
-	 * Once granted, an event listener is set on any position update, so it can update the
-	 * map state and the markers position. This method can be called again, only if the
-	 * geolocation watch has been cleared ; for example when updating the accuracy options.
-	 * </blockquote>
-	 * @returns {Promise} A Promise resolved when preferences are set
-	 **/
-	_initGeolocation() {
-		return new Promise(resolve => {
-			if ('geolocation' in navigator) {
-				const options = (Utils.getPreference('map-high-accuracy') === 'true') ? Utils.HIGH_ACCURACY : Utils.OPTIMIZED_ACCURACY;
-				this._watchId = navigator.geolocation.watchPosition(position => {
-					// Update saved user position
-					this._user.lat = position.coords.latitude;
-					this._user.lng = position.coords.longitude;
-					this._user.accuracy = position.coords.accuracy;
-					// Only draw marker if map is already created
-					if (this._map) {
-						this.drawUserMarker();
-						this.updateMarkerCirclesVisibility();
-						// Update map position if focus lock is active
-						if (Utils.getPreference('map-center-on-user') === 'true' && !this._isZooming) {
-							this._map.setView(this._user);
-            }
-						// Updating debug info
-						this.updateDebugUI();
-					}
-					resolve();
-				}, resolve, options);
-      } else {
-				this._notification.raise(this.nls.notif('geolocationError'));
-				resolve();
-			}
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name _initMap
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite),
-	 * add scale control, remove zoom control and set map bounds.
-	 * </blockquote>
-	 * @returns {Promise} A Promise resolved when preferences are set
-	 **/
-	_initMap() {
-		return new Promise(resolve => {
-			// Use main div to inject OSM into
-			this._map = window.L.map('beer-crakerz-map', {
-				zoomControl: false,
-			}).setView([this._user.lat, this._user.lng], 18);
-			// Add meter and feet scale on map
-			window.L.control.scale().addTo(this._map);
-			// Place user marker on the map
-			this.drawUserMarker();
-			// Add OSM credits to the map next to leaflet credits
-			const osm = window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
-				attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
-				maxZoom: 21,
-				maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19
-				minZoom: 2 // Don't allow dezooming too far from map so it always stay fully visible
-			});
-			const esri = window.L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
-				attribution: '&copy; <a href="https://www.arcgis.com/home/item.html?id=10df2279f9684e4a9f6a7f08febac2a9">Esri Imagery</a>',
-				maxZoom: 21,
-				maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19
-				minZoom: 2 // Don't allow dezooming too far from map so it always stay fully visible
-			});
-			// Prevent panning outside of the world's edge
-			this._map.setMaxBounds(Utils.MAP_BOUNDS);
-			// Add layer group to interface
-			const baseMaps = {};
-			baseMaps[`<p>${this.nls.map('planLayer')}</p>`] = osm;
-			baseMaps[`<p>${this.nls.map('satLayer')}</p>`] = esri;
-			// Append layer depending on user preference
-			if (Utils.getPreference('map-plan-layer') === 'true') {
-				osm.addTo(this._map);
-      } else {
-				esri.addTo(this._map);
-			}
-			// Add layer switch radio on bottom right of the map
-			window.L.control.layers(baseMaps, {}, { position: 'bottomright' }).addTo(this._map);
-			// Init zoom slider when map has been created
-			this._zoomSlider = new ZoomSlider(this._map);			
-			resolve();
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name _initEvents
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _initEvents() method will listen to all required events to manipulate the map. Those events
-	 * are both for commands and for map events (click, drag, zoom and layer change).
-	 * </blockquote>
-	 * @returns {Promise} A Promise resolved when preferences are set
-	 **/
-	_initEvents() {
-		return new Promise(resolve => {
-			// Command events
-			document.getElementById('user-profile').addEventListener('click', this.userProfileModal.bind(this));
-			document.getElementById('about').addEventListener('click', this.aboutModal.bind(this));
-			document.getElementById('hide-show').addEventListener('click', this.hidShowModal.bind(this));
-			document.getElementById('center-on').addEventListener('click', this.toggleFocusLock.bind(this));
-			document.getElementById('overlay').addEventListener('click', this.closeModal.bind(this));
-			// Subscribe to click event on map to react
-			this._map.on('click', this.mapClicked.bind(this));
-			// Map is dragged by user mouse/finger
-			this._map.on('drag', () => {
-				// Constrain pan to the map bounds
-				this._map.panInsideBounds(Utils.MAP_BOUNDS, { animate: true });
-				// Disable lock focus if user drags the map
-				if (Utils.getPreference('map-center-on-user') === 'true') {
-					this.toggleFocusLock();
-				}
-			});
-			// Map events
-			this._map.on('zoomstart', () => {
-				this._isZooming = true;
-				if (Utils.getPreference('poi-show-circle') === 'true') {
-					this.setMarkerCircles(this._marks.spot, false);
-					this.setMarkerCircles(this._marks.store, false);
-					this.setMarkerCircles(this._marks.bar, false);
-					this.setMarkerCircles([this._user], false);
-					this.setMarkerCircles([{ circle: this._user.range }], false);
-				}
-			});
-			this._map.on('zoomend', () => {
-        this._isZooming = false;
-				if (Utils.getPreference('poi-show-circle') === 'true') {
-					if (this._map.getZoom() >= 15) {
-						this.setMarkerCircles(this._marks.spot, true);
-						this.setMarkerCircles(this._marks.store, true);
-						this.setMarkerCircles(this._marks.bar, true);
-						this.setMarkerCircles([this._user], true);
-						this.setMarkerCircles([{ circle: this._user.range }], true);
-					}
-				}
-				// Auto hide labels if zoom level is too high (and restore it when needed)
-				if (Utils.getPreference('poi-marker-label') === 'true') {
-					if (this._map.getZoom() < 15) {
-						this.setMarkerLabels(this._marks.spot, false);
-						this.setMarkerLabels(this._marks.store, false);
-						this.setMarkerLabels(this._marks.bar, false);
-					} else {
-						this.setMarkerLabels(this._marks.spot, true);
-            this.setMarkerLabels(this._marks.store, true);
-            this.setMarkerLabels(this._marks.bar, true);
-					}
-				}
-        // Updating debug info
-        this.updateDebugUI();
-      });
-			this._map.on('baselayerchange', event => {
-				const planActive = (Utils.stripDom(event.name) === this.nls.map('planLayer'));
-				Utils.setPreference('map-plan-layer', planActive);
-      });
-			resolve();
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name _initMarkers
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _initEvents() method will initialize all saved marker into the map. 
-	 * Markers must be retrieved from server with a specific format to ensure it works
-	 * </blockquote>
-	 * @returns {Promise} A Promise resolved when preferences are set
-	 **/	
-	_initMarkers() {
-		return new Promise(resolve => {
-			// Init map clusters for marks to be displayed (disable clustering at opened popup zoom level)
-			const clusterOptions = {
-				animateAddingMarkers: true,
-				disableClusteringAtZoom: 18,
-				spiderfyOnMaxZoom: false
-			};
-			this._clusters.spot = new window.L.MarkerClusterGroup(clusterOptions);
-			this._clusters.store = new window.L.MarkerClusterGroup(clusterOptions);
-			this._clusters.bar = new window.L.MarkerClusterGroup(clusterOptions);
-			// Append clusters to the map depending on user preferences
-			if (Utils.getPreference(`poi-show-spot`) === 'true') {
-				this._map.addLayer(this._clusters.spot);
-			}
-			if (Utils.getPreference(`poi-show-store`) === 'true') {
-        this._map.addLayer(this._clusters.store);
-      }
-			if (Utils.getPreference(`poi-show-bar`) === 'true') {
-        this._map.addLayer(this._clusters.bar);
-      }
-			// Load data from local storage, later to be fetched from server
-			const iterateMarkers = mark => {
-        this.markPopupFactory(mark).then(dom => {
-          mark.dom = dom;
-          mark.marker = this.placeMarker(mark);
-					this._marks[mark.type].push(mark);
-					this._clusters[mark.type].addLayer(mark.marker);
-				});
-			};
-			let marks = JSON.parse(Utils.getPreference('saved-spot')) || [];
-			for (let i = 0; i < marks.length; ++i) {
-				iterateMarkers(marks[i]);
-			}
-			marks = JSON.parse(Utils.getPreference('saved-store')) || [];
-			for (let i = 0; i < marks.length; ++i) {
-				iterateMarkers(marks[i]);
-			}
-			marks = JSON.parse(Utils.getPreference('saved-bar')) || [];
-			for (let i = 0; i < marks.length; ++i) {
-				iterateMarkers(marks[i]);
-			}
-
-			resolve();
-		});
-	}
-
-
-  // ======================================================================== //
-  // ------------------------- Toggle for map items ------------------------- //
-  // ======================================================================== //	
-
-
-	/**
-	 * @method
-   * @name toggleFocusLock
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The toggleFocusLock() method will, depending on user preference, lock or unlock
-	 * the map centering around the user marker at each position refresh. This way the user
-	 * can roam while the map is following its position. 
-	 * </blockquote>
-	 **/
-	toggleFocusLock() {
-		if (Utils.getPreference('map-center-on-user') === 'true') {
-      document.getElementById('center-on').classList.remove('lock-center-on');
-      Utils.setPreference('map-center-on-user', 'false');
-    } else {
-      document.getElementById('center-on').classList.add('lock-center-on');
-      this._map.flyTo([this._user.lat, this._user.lng], 18);
-      Utils.setPreference('map-center-on-user', 'true');
-    }
-	}
-
-
-	/**
-	 * @method
-   * @name toggleLabel
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The toggleLabel() method will, depending on user preference, display or not
-	 * the labels attached to spots/stores/bars marks. This label is basically the
-	 * mark name given by its creator.
-	 * </blockquote>
-	 **/
-	toggleLabel() {
-		const visible = !(Utils.getPreference('poi-marker-label') === 'true');
-		this.setMarkerLabels(this._marks.spot, visible);
-		this.setMarkerLabels(this._marks.store, visible);
-		this.setMarkerLabels(this._marks.bar, visible);
-		Utils.setPreference('poi-marker-label', visible);
-	}
-
-
-	/**
-	 * @method
-   * @name toggleCircle
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The toggleCircle() method will, depending on user preference, display or not
-	 * the circles around the spots/stores/bars marks. This circle indicates the minimal
-	 * distance which allow the user to make updates on the mark information
-	 * </blockquote>
-	 **/
-	toggleCircle() {
-		const visible = !(Utils.getPreference('poi-show-circle') === 'true');
-		this.setMarkerCircles(this._marks.spot, visible);
-		this.setMarkerCircles(this._marks.store, visible);
-		this.setMarkerCircles(this._marks.bar, visible);
-		this.setMarkerCircles([this._user], visible);
-		this.setMarkerCircles([{ circle: this._user.range }], visible);
-		Utils.setPreference('poi-show-circle', visible);
-	}
-
-
-	/**
-	 * @method
-   * @name toggleMarkers
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The toggleMarkers() method will, depending on user preference, display or not
-	 * a given mark type. This way, the user can fine tune what is displayed on the map.
-	 * A mark type in spots/stores/bars must be given as an argument
-	 * </blockquote>
-	 * @param {String} type The mark type in spots/tores/bars
-	 **/
-	toggleMarkers(type) {
-		const visible = !(Utils.getPreference(`poi-show-${type}`) === 'true');
-		if (visible === true) {
-			this._map.addLayer(this._clusters[type]);
-		} else {
-			this._map.removeLayer(this._clusters[type]);
-		}
-		Utils.setPreference(`poi-show-${type}`, visible);
-	}
-
-
-	/**
-	 * @method
-   * @name toggleHighAccuracy
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The toggleHighAccuracy() method will, depending on user preference, update the
-	 * geolocation accuracy between optimized and high. The high settings might cause
-	 * more memory and processing consumption, but gives better results. It will clear
-	 * any previous position watch on the geolocation API so it can subscribe a new one
-	 * with the new accuracy parameters (see Utils for values)
-	 * </blockquote>
-	 **/
-	toggleHighAccuracy() {
-		const high = !(Utils.getPreference('map-high-accuracy') === 'true');
-		Utils.setPreference('map-high-accuracy', high);
-		navigator.geolocation.clearWatch(this._watchId);
-		this._initGeolocation().then(this.updateDebugUI.bind(this));
-	}
-
-
-	/**
-	 * @method
-   * @name toggleDebug
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The toggleDebug() method will, depending on user preference, add or remove
-	 * the debug DOM element to the user interface. The debug DOM display several
-	 * useful information to identify an issue with the geolocation API
-	 * </blockquote>
-	 **/
-	toggleDebug() {
-		const visible = !window.DEBUG;
-		window.DEBUG = visible;
-		Utils.setPreference('app-debug', visible);
-		if (visible) {
-			this.addDebugUI();
-		} else {
-			this.removeDebugUI();
-		}
-	}
-
-
-  // ======================================================================== //
-  // ----------------- App modals display and interaction ------------------- //
-  // ======================================================================== //	
-
-
-	newMarkModal(dom) {
-		document.getElementById('overlay').appendChild(dom);
-		document.getElementById('overlay').style.display = 'flex';
-		setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50);
-	}
-
-
-	editMarkModal(options) {	
-		Utils.fetchTemplate(`assets/html/modal/edit${options.type}.html`).then(dom => {
-			const name = dom.querySelector(`#${options.type}-name`);
-      const description = dom.querySelector(`#${options.type}-desc`);
-			const submit = dom.querySelector(`#${options.type}-submit`);
-			const cancel = dom.querySelector(`#${options.type}-cancel`);
-			const rate = dom.querySelector(`#${options.type}-rating`);
-			const rating = new Rating(rate, options.rate);
-			// Update nls for template
-			Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal(`${options.type}EditTitle`));
-      Utils.replaceString(dom.querySelector(`#nls-${options.type}-name`), `{{${options.type.toUpperCase()}_NAME}}`, this.nls[options.type]('nameLabel'));
-      Utils.replaceString(dom.querySelector(`#nls-${options.type}-desc`), `{{${options.type.toUpperCase()}_DESC}}`, this.nls[options.type]('descLabel'));
-      Utils.replaceString(dom.querySelector(`#nls-${options.type}-rate`), `{{${options.type.toUpperCase()}_RATE}}`, this.nls[options.type]('rateLabel'));
-      Utils.replaceString(submit, `{{${options.type.toUpperCase()}_SUBMIT}}`, this.nls.nav('add'));
-      Utils.replaceString(cancel, `{{${options.type.toUpperCase()}_CANCEL}}`, this.nls.nav('cancel'));
-			name.value = options.name;		
-			description.value = options.description;
-			submit.addEventListener('click', () => {
-				// Iterate through marks to find matching one (by coord as marks coordinates are unique)
-				for (let i = 0; i < this._marks[options.type].length; ++i) {
-          // We found, remove circle, label and marker from map/clusters
-          if (options.lat === this._marks[options.type][i].lat && options.lng === this._marks[options.type][i].lng) {
-            this._marks[options.type][i].name = name.value;
-            this._marks[options.type][i].description = description.value;
-            this._marks[options.type][i].rate = rating.currentRate;
-						options.tooltip.removeFrom(this.map);
-						this.markPopupFactory(options).then(dom => {
-							options.dom = dom;
-							options.marker.setPopupContent(options.dom);
-						});
-            break;
-          }
-        }
-				// Format marks to be saved and then update user preference with
-				const formattedMarks = [];
-				for (let i = 0; i < this._marks[options.type].length; ++i) {
-					formattedMarks.push(this.formatSavedMarker(this._marks[options.type][i]));
-				}
-				Utils.setPreference(`saved-${options.type}`, JSON.stringify(formattedMarks));
-				// Notify user through UI that marker has been successfully deleted
-				this._notification.raise(this.nls.notif(`${options.type}Deleted`));
-				this.closeModal(null, true);
-      });
-
-			cancel.addEventListener('click', this.closeModal.bind(this, null, true));
-			document.getElementById('overlay').appendChild(dom);
-      document.getElementById('overlay').style.display = 'flex';
-			setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50);
-		});	
-	}
-
-
-	/**
-	 * @method
-   * @name deleteMarkModal
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since February 2022
-   * @description
-	 * <blockquote>
-	 * The deleteMarkModal() method will request the mark delete modal, which prompts
-	 * the user a confirmation to actually delete the mark
-	 * </blockquote>
-	 * @param {Function} cb The function to callback with true or false depending on user's choice
-	 **/
-	deleteMarkModal(cb) {
-		Utils.fetchTemplate('assets/html/modal/deletemark.html').then(dom => {
-			// Update nls for template
-			Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('deleteMarkTitle'));
-      Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{{MODAL_DESC}}`, this.nls.modal('deleteMarkDesc'));
-      Utils.replaceString(dom.querySelector(`#cancel-close`), `{{MODAL_CANCEL}}`, this.nls.nav('cancel'));
-      Utils.replaceString(dom.querySelector(`#delete-close`), `{{MODAL_DELETE}}`, this.nls.nav('delete'));
-			document.getElementById('overlay').appendChild(dom);
-      document.getElementById('overlay').style.display = 'flex';
-			// Setup callback for confirm/cancel buttons
-			document.getElementById('cancel-close').addEventListener('click', e => {
-				this.closeModal(e);
-				cb(false);
-			}, false);
-			document.getElementById('delete-close').addEventListener('click', e => {
-				this.closeModal(e);
-				cb(true);
-			}, false);			
-			setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50);		
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name userProfileModal
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The userProfileModal() method will request the user modal, which contains
-	 * the user preferences, and the user profile information
-	 * </blockquote>
-	 **/
-	userProfileModal() {
-		Utils.fetchTemplate('assets/html/modal/user.html').then(dom => {
-			// Update nls for template
-      Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('userTitle'));
-      Utils.replaceString(dom.querySelector(`#nls-user-modal-accuracy`), `{{ACCURACY_USER_MODAL}}`, this.nls.modal('userAccuracyPref'));
-      Utils.replaceString(dom.querySelector(`#nls-user-modal-debug`), `{{DEBUG_USER_MODAL}}`, this.nls.modal('userDebugPref'));
-
-			document.getElementById('overlay').appendChild(dom);
-      document.getElementById('overlay').style.display = 'flex';
-			// Init modal checkbox state according to local storage preferences
-			if (Utils.getPreference('map-high-accuracy') === 'true') {
-        document.getElementById('high-accuracy-toggle').checked = true;
-      }
-
-			if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) {
-        document.getElementById('debug-toggle').checked = true;
-      }
-
-			document.getElementById('high-accuracy-toggle').addEventListener('change', this.toggleHighAccuracy.bind(this));
-			document.getElementById('debug-toggle').addEventListener('change', this.toggleDebug.bind(this));
-
-			setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50);
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name aboutModal
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The aboutModal() method will request the about modal, which contains
-	 * information about BeerCrackerz, cookies/tracking policies and links
-	 * </blockquote>
-	 **/
-	aboutModal() {
-		Utils.fetchTemplate('assets/html/modal/about.html').then(dom => {
-			// Update nls for template
-			Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('aboutTitle'));
-      Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{{MODAL_DESC}}`, this.nls.modal('aboutDesc'));
-			
-			document.getElementById('overlay').appendChild(dom);
-			document.getElementById('overlay').style.display = 'flex';
-			setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50);
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name hidShowModal
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The hidShowModal() method will request the hide show modal, which all
-	 * toggles for map elements ; labels/circles/spots/stores/bars
-	 * </blockquote>
-	 **/
-	hidShowModal() {
-		Utils.fetchTemplate('assets/html/modal/hideshow.html').then(dom => {
-			// Update template nls
-			Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('hideShowTitle'));
-			Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-labels`), `{{LABELS_HIDESHOW_MODAL}}`, this.nls.modal('hideShowLabels'));
-			Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-circles`), `{{CIRCLES_HIDESHOW_MODAL}}`, this.nls.modal('hideShowCircles'));
-			Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-spots`), `{{SPOTS_HIDESHOW_MODAL}}`, this.nls.modal('hideShowSpots'));
-			Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-stores`), `{{STORES_HIDESHOW_MODAL}}`, this.nls.modal('hideShowStores'));
-			Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-bars`), `{{BARS_HIDESHOW_MODAL}}`, this.nls.modal('hideShowBars'));
-			Utils.replaceString(dom.querySelector(`#modal-close-button`), `{{MODAL_CLOSE}}`, this.nls.nav('close'));
-			document.getElementById('overlay').appendChild(dom);
-			document.getElementById('overlay').style.display = 'flex';
-			// Init modal checkbox state according to local storage preferences
-			if (Utils.getPreference('poi-marker-label') === 'true') {
-        document.getElementById('label-toggle').checked = true;
-      }
-
-			if (Utils.getPreference('poi-show-circle') === 'true') {
-        document.getElementById('circle-toggle').checked = true;
-      }
-
-			if (Utils.getPreference('poi-show-spot') === 'true') {
-        document.getElementById('show-spots').checked = true;
-      }
-
-			if (Utils.getPreference('poi-show-store') === 'true') {
-        document.getElementById('show-stores').checked = true;
-      }
-
-			if (Utils.getPreference('poi-show-bar') === 'true') {
-        document.getElementById('show-bars').checked = true;
-      }
-
-			document.getElementById('label-toggle').addEventListener('change', this.toggleLabel.bind(this));
-			document.getElementById('circle-toggle').addEventListener('change', this.toggleCircle.bind(this));
-			document.getElementById('show-spots').addEventListener('change', this.toggleMarkers.bind(this, 'spot'));
-			document.getElementById('show-stores').addEventListener('change', this.toggleMarkers.bind(this, 'store'));
-			document.getElementById('show-bars').addEventListener('change', this.toggleMarkers.bind(this, 'bar'));
-
-			setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50);
-		});
-	}
-
-
-	/**
-	 * @method
-   * @name closeModal
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The closeModal() method will close any opened modal if the click event is
-	 * targeted on the modal overlay or on close buttons 
-	 * </blockquote>
-	 * @param {Event} event The click event
-	 **/
-	closeModal(event, force) {
-		if (force === true || event.target.id === 'overlay' || event.target.id.indexOf('close') !== -1) {
-      document.getElementById('overlay').style.opacity = 0;
-      setTimeout(() => {
-        document.getElementById('overlay').style.display = 'none';
-        document.getElementById('overlay').innerHTML = '';
-      }, 300);
-    }
-	}
-
-
-  // ======================================================================== //
-  // -------------------------- Map interaction ----------------------------- //
-  // ======================================================================== //
-
-
-	/**
-	 * @method
-   * @name mapClicked
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The mapClicked() method is the callback used when the user clicked on the Leaflet.js map
-	 * </blockquote>
-	 * @param {Event} event The click event
-	 **/
-	mapClicked(event) {
-		if (this._newMarker && this._newMarker.popupClosed) {
-			// Avoid to open new marker right after popup closing
-			this._newMarker = null;
-		} else if (this._newMarker === null || !this._newMarker.isBeingDefined) {
-			// Only create new marker if none is in progress, and that click is max range to add a marker
-			const distance = Utils.getDistanceBetweenCoords([this._user.lat, this._user.lng], [event.latlng.lat, event.latlng.lng]);
-			if (distance < Utils.NEW_MARKER_RANGE) {
-				this._newMarker = this.definePOI(event.latlng, this._markerSaved.bind(this));
-			} else {
-				this._notification.raise(this.nls.notif('newMarkerOutside'));
-			}
-		}
-	}
-
-
-	/**
-	 * @method
-   * @name _markerSaved
-   * @private
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The _markerSaved() method is the callback used when a marker is created and added
-	 * to the map. It is the last method of a new marker proccess.
-	 * </blockquote>
-	 * @param {Object} options The new marker options
-	 **/
-	_markerSaved(options) {
-		// Save marke in marks and clusters for the map
-		this._marks[options.type].push(options);
-		this._clusters[options.type].addLayer(options.marker);
-		// Notify user that new marker has been saved
-		this._notification.raise(this.nls.notif(`${options.type}Added`));
-		// Update marker circles visibility according to user position
-		this.updateMarkerCirclesVisibility();
-		// Clear new marker to let user add other stuff
-		this._newMarker = null;
-		// Save new marker in local storage, later to be sent to the server
-		const marks = JSON.parse(Utils.getPreference(`saved-${options.type}`)) || [];
-		marks.push(this.formatSavedMarker(options));
-		Utils.setPreference(`saved-${options.type}`, JSON.stringify(marks));
-	}
-
-
-	/**
-	 * @method
-   * @name updateMarkerCirclesVisibility
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The updateMarkerCirclesVisibility() method will update the circle visibility for
-	 * all mark types (spots/stores/bars) and for the user marker
-	 * </blockquote>
-	 **/
-	updateMarkerCirclesVisibility() {
-		const _updateByType = data => {
-			// Check spots in user's proximity
-			for (let i = 0; i < data.length; ++i) {
-				// Only update circles that are in user view
-				if (this._map.getBounds().contains(data[i].marker.getLatLng())) {
-					const marker = data[i].marker;
-					const distance = Utils.getDistanceBetweenCoords([this._user.lat, this._user.lng], [marker.getLatLng().lat, marker.getLatLng().lng]);
-					// Only show if user distance to marker is under circle radius
-					if (distance < Utils.CIRCLE_RADIUS && !data[i].circle.visible) {
-						data[i].circle.visible = true;
-						data[i].circle.setStyle({
-							opacity: 1,
-							fillOpacity: 0.1
-						});
-					} else if (distance >= Utils.CIRCLE_RADIUS && data[i].circle.visible) {
-						data[i].circle.visible = false;
-						data[i].circle.setStyle({
-							opacity: 0,
-							fillOpacity: 0
-						});
-					}
-				}
-			}
-		};
-
-		if (Utils.getPreference('poi-show-circle') === 'true') {
-      _updateByType(this._marks.spot);
-      _updateByType(this._marks.store);
-      _updateByType(this._marks.bar);
-      _updateByType([this._user]);
-    }
-	}
-
-
-  // ======================================================================== //
-  // -------------------------- Marker edition ------------------------------ //
-  // ======================================================================== //
-
-
-	/**
-	 * @method
-   * @name formatSavedMarker
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since February 2022
-   * @description
-	 * <blockquote>
-	 * This method formats a mark returned from MapHelper so it can be parsed
-	 * using JSON.parse (in order to store it in local storage/database)
-	 * </blockquote>
-	 * @param {Object} mark The mark options from internal this._marks[type]
-	 **/
-	formatSavedMarker(mark) {
-		return {
-      type: mark.type,
-      lat: mark.lat,
-      lng: mark.lng,
-      name: mark.name,
-      description: mark.description,
-      user: mark.username || this.user.username,
-      userId: mark.userId || this.user.id,
-      dom: null,
-      rate: mark.rate,
-      marker: null,
-      circle: null,
-    };
-	}
-
-
-	/**
-	 * @method
-   * @name editMarker
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since February 2022
-   * @description
-	 * <blockquote>
-	 * This method will open a mark edition modal
-	 * </blockquote>
-	 * @param {Object} options The mark options to edit
-	 **/
-	editMarker(options) {
-		this._map.closePopup();
-		this.editMarkModal(options);
-	}
-
-
-	/**
-	 * @method
-   * @name deleteMarker
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since February 2022
-   * @description
-	 * <blockquote>
-	 * This method will delete a mark after prompting the user if he trully wants to
-	 * </blockquote>
-	 * @param {Object} options The mark options to delete
-	 **/	
-	deleteMarker(options) {
-		this.deleteMarkModal(confirm => {
-			if (confirm === true) {
-				// Iterate through marks to find matching one (by coord as marks coordinates are unique)
-				const marks = this._marks[options.type];
-				for (let i = 0; i < marks.length; ++i) {
-					// We found, remove circle, label and marker from map/clusters
-					if (options.lat === marks[i].lat && options.lng === marks[i].lng) {
-						this.setMarkerCircles([ marks[i] ], false);
-						this.setMarkerLabels([marks[i]], false);
-						this._clusters[options.type].removeLayer(marks[i].marker);
-						marks.splice(i, 1);
-						break;
-					}
-				}
-				// Update internal marks array
-				this._marks[options.type] = marks;
-				// Format marks to be saved and then update user preference with
-				const formattedMarks = [];
-				for (let i = 0; i < this._marks[options.type].length; ++i) {
-					formattedMarks.push(this.formatSavedMarker(this._marks[options.type][i]));
-				}
-				Utils.setPreference(`saved-${options.type}`, JSON.stringify(formattedMarks));
-				// Notify user through UI that marker has been successfully deleted
-				this._notification.raise(this.nls.notif(`${options.type}Deleted`));				
-			}
-		});
-	}
-
-
-  // ======================================================================== //
-  // ---------------------------- Debug methods ----------------------------- //
-  // ======================================================================== //	
-
-
-	/**
-	 * @method
-   * @name addDebugUI
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The addDebugUI() method appends the debug DOM element to the document body
-	 * </blockquote>
-	 **/
-	addDebugUI() {
-		document.body.appendChild(this._debugElement);
-	}
-
-
-	/**
-	 * @method
-   * @name removeDebugUI
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The removeDebugUI() method remove the debug DOM element from the document body
-	 * </blockquote>
-	 **/
-	removeDebugUI() {
-		document.body.removeChild(this._debugElement);
-	}
-
-
-	/**
-	 * @method
-   * @name updateDebugUI
-   * @public
-   * @memberof BeerCrackerz
-   * @author Arthur Beaulieu
-	 * @since January 2022
-   * @description
-	 * <blockquote>
-	 * The updateDebugUI() method will update informations held in the debug DOM
-	 * </blockquote>
-	 **/
-	updateDebugUI() {
-		const options = (Utils.getPreference('map-high-accuracy') === 'true') ? Utils.HIGH_ACCURACY : Utils.OPTIMIZED_ACCURACY;
-		Utils.updateDebugInterface(this._debugElement, this._user, options);
-	}
-
-
-  // ======================================================================== //
-  // ---------------------------- Class accessors --------------------------- //
-  // ======================================================================== //	
-
-
-	/**
-	 * @public
-	 * @property {Object} map
-	 * Leaflet.js map getter
-	 **/
-	get map() {
-		return this._map;
-	}
-
-
-	/**
-	 * @public
-	 * @property {Object} marks
-	 * Leaflet.js marks that holds spot/store/bar marks as subkeys
-	 **/	
-	get marks() {
-		return this._marks;
-	}
-
-
-	/**
-	 * @public
-	 * @property {Object} user
-	 * The session user object
-	 **/
-	get user() {
-		return this._user;
-	}
-
-
-	/**
-	 * @public
-	 * @property {Object} nls
-	 * The LangManager getter
-	 **/
-	get nls() {
-		return this._lang;
-	}	
-
-
-}
-
-
-export default BeerCrackerz;
-
-
-
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.10 on Fri Feb 11 2022 16:38:03 GMT+0100 (GMT+01:00) -
- - - - - diff --git a/doc/beercrackerz/0.0.1/Utils.js.html b/doc/beercrackerz/0.0.1/Utils.js.html deleted file mode 100644 index 1504a54..0000000 --- a/doc/beercrackerz/0.0.1/Utils.js.html +++ /dev/null @@ -1,264 +0,0 @@ - - - - - JSDoc: Source: Utils.js - - - - - - - - - - -
- -

Source: Utils.js

- - - - - - -
-
-
class Utils {
-
-
-  constructor() { /* Not meant to be instantiated, all methods should be static */ }
-
-
-  static fetchTemplate(url) {
-    return new Promise((resolve, reject) => {
-			fetch(url).then(data => {
-        data.text().then(html => {
-          resolve(document.createRange().createContextualFragment(html));
-        }).catch(reject);
-			}).catch(reject);
-		});
-  }
-
-
-  static fetchFile(url) {
-    return new Promise((resolve, reject) => {
-			fetch(url).then(data => {
-        data.text().then(string => {
-          resolve(string);
-        }).catch(reject);
-			}).catch(reject);
-		});    
-  }
-
-
-  static stripDom(html){
-    let doc = new DOMParser().parseFromString(html, 'text/html');
-    return doc.body.textContent || '';
-  }
-
-
-  static replaceString(element, string, value) {
-    element.innerHTML = element.innerHTML.replace(string, value);
-  }
-
-
-  static getDistanceBetweenCoords(from, to) {
-    // return distance in meters
-    var lon1 = (from[1] * Math.PI) / 180,
-      lat1 = (from[0] * Math.PI) / 180,
-      lon2 = (to[1] * Math.PI) / 180,
-      lat2 = (to[0] * Math.PI) / 180;
-
-    var deltaLat = lat2 - lat1;
-    var deltaLon = lon2 - lon1;
-
-    var a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2);
-    var c = 2 * Math.asin(Math.sqrt(a));
-    var EARTH_RADIUS = 6371;
-    return c * EARTH_RADIUS * 1000;
-  }
-
-
-  /** @method
-   * @name precisionRound
-   * @public
-   * @memberof Utils
-   * @author Arthur Beaulieu
-   * @since September 2018
-   * @description Do a Math.round with a given precision (ie amount of integers after the coma)
-   * @param {nunmber} value - The value to precisely round
-   * @param {number} precision - The number of integers after the coma
-   * @return {number} - The rounded value */
-  static precisionRound(value, precision) {
-    const multiplier = Math.pow(10, precision || 0);
-    return Math.round(value * multiplier) / multiplier;
-  }
-
-
-  static initDebugInterface() {
-    const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls);
-    const debugContainer = document.createElement('DIV');
-    const userLat = document.createElement('P');
-    const userLng = document.createElement('P');
-    const updatesAmount = document.createElement('P');
-    const userAccuracy = document.createElement('P');
-    const highAccuracy = document.createElement('P');
-    const maxAge = document.createElement('P');
-    const posTimeout = document.createElement('P');
-    const zoomLevel = document.createElement('P');
-    debugContainer.classList.add('debug-container');
-    userLat.classList.add('debug-user-lat');
-    userLng.classList.add('debug-user-lng');
-    updatesAmount.classList.add('debug-updates-amount');
-    userAccuracy.classList.add('debug-user-accuracy');
-    highAccuracy.classList.add('debug-high-accuracy');
-    maxAge.classList.add('debug-pos-max-age');
-    posTimeout.classList.add('debug-pos-timeout');
-    zoomLevel.classList.add('debug-zoom-level');
-    userLat.innerHTML = `<b>${lang('lat')}</b> : -`;
-    userLng.innerHTML = `<b>${lang('lng')}</b> : -`;
-    updatesAmount.innerHTML = `<b>${lang('updates')}</b> : 0`;
-    userAccuracy.innerHTML = `<b>${lang('accuracy')}</b> : -`;
-    highAccuracy.innerHTML = `<b>${lang('highAccuracy')}</b> : -`;
-    maxAge.innerHTML = `<b>${lang('posAge')}</b> : -`;
-    posTimeout.innerHTML = `<b>${lang('posTimeout')}</b> : -`;
-    zoomLevel.innerHTML = `<b>${lang('zoom')}</b> : -`;
-    debugContainer.appendChild(userLat);
-    debugContainer.appendChild(userLng);
-    debugContainer.appendChild(updatesAmount);
-    debugContainer.appendChild(userAccuracy);
-    debugContainer.appendChild(highAccuracy);
-    debugContainer.appendChild(maxAge);
-    debugContainer.appendChild(posTimeout);
-    debugContainer.appendChild(zoomLevel);
-    return debugContainer;
-  }
-
-
-  static updateDebugInterface(element, user, options) {
-    if (window.DEBUG === true) {
-      const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls);
-      const updates = parseInt(element.querySelector('.debug-updates-amount').innerHTML.split(' : ')[1]) + 1;
-      element.querySelector('.debug-user-lat').innerHTML = `
-        <b>${lang('lat')}</b> : ${user.lat}
-      `;
-      element.querySelector('.debug-user-lng').innerHTML = `
-        <b>${lang('lng')}</b> : ${user.lng}
-      `;
-      element.querySelector('.debug-updates-amount').innerHTML = `
-        <b>${lang('updates')}</b> : ${updates}
-      `;
-      element.querySelector('.debug-user-accuracy').innerHTML = `
-        <b>${lang('accuracy')}</b> : ${Utils.precisionRound(user.accuracy, 2)}m
-      `;
-      element.querySelector('.debug-high-accuracy').innerHTML = `
-        <b>${lang('highAccuracy')}</b> : ${options.enableHighAccuracy === true ? lang('enabled') : lang('disabled')}
-      `;
-      element.querySelector('.debug-pos-max-age').innerHTML = `
-        <b>${lang('posAge')}</b> : ${options.maximumAge / 1000}s
-      `;
-      element.querySelector('.debug-pos-timeout').innerHTML = `
-        <b>${lang('posTimeout')}</b> : ${options.timeout / 1000}s
-      `;
-      element.querySelector('.debug-zoom-level').innerHTML = `
-        <b>${lang('zoom')}</b> : ${window.BeerCrackerz.map.getZoom()}
-      `;
-    }
-  }
-
-
-  static getPreference(pref) {
-    return localStorage.getItem(pref) || null;
-  }
-
-
-  static setPreference(pref, value) {
-    localStorage.setItem(pref, value);
-  }
-
-
-  static get USER_COLOR() {
-    return '#63fff5';
-  }
-
-
-  static get SPOT_COLOR() {
-    return '#26ad23';
-  }
-
-
-  static get STORE_COLOR() {
-    return '#247dc9';
-  }
-
-
-  static get BAR_COLOR() {
-    return '#ca2a3d';
-  }
-
-
-  static get CIRCLE_RADIUS() {
-    return 100;
-  }
-
-
-  static get NEW_MARKER_RANGE() {
-    return 200;
-  }
-
-
-  static get MAP_BOUNDS() {
-    return window.L.latLngBounds(
-      window.L.latLng(-89.98155760646617, -180),
-      window.L.latLng(89.99346179538875, 180)
-    );
-  }
-
-
-  static get HIGH_ACCURACY() {
-    return {
-      enableHighAccuracy: true, // More consuption, better position
-      maximumAge: 1000, // A position will last 1s maximum
-      timeout: 900, // A position is updated in 0.9s maximum
-    };
-  }
-
-
-  static get OPTIMIZED_ACCURACY() {
-    return {
-      enableHighAccuracy: false, // Less consuption
-      maximumAge: 30000, // A position will last 30s maximum
-      timeout: 29000 // A position is updated in 29s maximum
-    };
-  }
-
-}
-
-
-export default Utils;
-
-
-
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.7 on Fri Jan 14 2022 10:17:56 GMT+0100 (GMT+01:00) -
- - - - - diff --git a/doc/beercrackerz/0.0.1/global.html b/doc/beercrackerz/0.0.1/global.html deleted file mode 100644 index ea024b3..0000000 --- a/doc/beercrackerz/0.0.1/global.html +++ /dev/null @@ -1,561 +0,0 @@ - - - - - JSDoc: Global - - - - - - - - - - -
- -

Global

- - - - - - -
- -
- -

- - -
- -
-
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - -
- - - - - - - - - - - - - - -

Members

- - - -

(private) _debugElement :object

- - - - -
-

The debug DOM object

-
- - - -
Type:
-
    -
  • - -object - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

(private) _isZooming :boolean

- - - - -
-

Flag to know if a zoom action is occuring on map

-
- - - -
Type:
-
    -
  • - -boolean - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

(private) _marks :object

- - - - -
-

The stored marks for spots, stores and bars

-
- - - -
Type:
-
    -
  • - -object - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

(private) _newMarker :object

- - - - -
-

The temporary marker for new marks only

-
- - - -
Type:
-
    -
  • - -object - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

(private) _user :object

- - - - -
-

The user object holds everything useful to ensure a proper session

-
- - - -
Type:
-
    -
  • - -object - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

(private) _watchId :number

- - - - -
-

ID for geolocation watch callback

-
- - - -
Type:
-
    -
  • - -number - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.7 on Fri Jan 14 2022 11:03:18 GMT+0100 (GMT+01:00) -
- - - - - \ No newline at end of file diff --git a/doc/beercrackerz/0.0.1/index.html b/doc/beercrackerz/0.0.1/index.html deleted file mode 100644 index c5b903e..0000000 --- a/doc/beercrackerz/0.0.1/index.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - JSDoc: Home - - - - - - - - - - -
- -

Home

- - - - - - - - -

beercrackerz 0.0.1

- - - - - - - - - - - - - - - -
-

BeerCrackerz

-

The website for beer lover, to share the best spot to crack a beer, or to easily refill this beverage of the gods!

-

https://github.com/pointhi/leaflet-color-markers

-

https://leafletjs.com/

-

https://www.svgrepo.com/svg/287438/info

-

https://github.com/Leaflet/Leaflet.markercluster

-
- - - - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.10 on Fri Feb 11 2022 16:38:03 GMT+0100 (GMT+01:00) -
- - - - - \ No newline at end of file diff --git a/doc/beercrackerz/0.0.1/js_Utils.js.html b/doc/beercrackerz/0.0.1/js_Utils.js.html deleted file mode 100644 index a8e8926..0000000 --- a/doc/beercrackerz/0.0.1/js_Utils.js.html +++ /dev/null @@ -1,283 +0,0 @@ - - - - - JSDoc: Source: js/Utils.js - - - - - - - - - - -
- -

Source: js/Utils.js

- - - - - - -
-
-
class Utils {
-
-
-  constructor() { /* Not meant to be instantiated, all methods should be static */ }
-
-
-  static fetchTemplate(url) {
-    return new Promise((resolve, reject) => {
-			fetch(url).then(data => {
-        data.text().then(html => {
-          resolve(document.createRange().createContextualFragment(html));
-        }).catch(reject);
-			}).catch(reject);
-		});
-  }
-
-
-  static fetchFile(url) {
-    return new Promise((resolve, reject) => {
-			fetch(url).then(data => {
-        data.text().then(string => {
-          resolve(string);
-        }).catch(reject);
-			}).catch(reject);
-		});    
-  }
-
-
-  static stripDom(html){
-    let doc = new DOMParser().parseFromString(html, 'text/html');
-    return doc.body.textContent || '';
-  }
-
-
-  static replaceString(element, string, value) {
-    element.innerHTML = element.innerHTML.replace(string, value);
-  }
-
-
-  static getDistanceBetweenCoords(from, to) {
-    // return distance in meters
-    var lon1 = (from[1] * Math.PI) / 180,
-      lat1 = (from[0] * Math.PI) / 180,
-      lon2 = (to[1] * Math.PI) / 180,
-      lat2 = (to[0] * Math.PI) / 180;
-
-    var deltaLat = lat2 - lat1;
-    var deltaLon = lon2 - lon1;
-
-    var a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2);
-    var c = 2 * Math.asin(Math.sqrt(a));
-    var EARTH_RADIUS = 6371;
-    return c * EARTH_RADIUS * 1000;
-  }
-
-
-  /** @method
-   * @name precisionRound
-   * @public
-   * @memberof Utils
-   * @author Arthur Beaulieu
-   * @since September 2018
-   * @description Do a Math.round with a given precision (ie amount of integers after the coma)
-   * @param {nunmber} value - The value to precisely round
-   * @param {number} precision - The number of integers after the coma
-   * @return {number} - The rounded value */
-  static precisionRound(value, precision) {
-    const multiplier = Math.pow(10, precision || 0);
-    return Math.round(value * multiplier) / multiplier;
-  }
-
-
-  static initDebugInterface() {
-    const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls);
-    const debugContainer = document.createElement('DIV');
-    const userLat = document.createElement('P');
-    const userLng = document.createElement('P');
-    const updatesAmount = document.createElement('P');
-    const userAccuracy = document.createElement('P');
-    const highAccuracy = document.createElement('P');
-    const maxAge = document.createElement('P');
-    const posTimeout = document.createElement('P');
-    const zoomLevel = document.createElement('P');
-    const marks = document.createElement('P');
-    debugContainer.classList.add('debug-container');
-    userLat.classList.add('debug-user-lat');
-    userLng.classList.add('debug-user-lng');
-    updatesAmount.classList.add('debug-updates-amount');
-    userAccuracy.classList.add('debug-user-accuracy');
-    highAccuracy.classList.add('debug-high-accuracy');
-    maxAge.classList.add('debug-pos-max-age');
-    posTimeout.classList.add('debug-pos-timeout');
-    zoomLevel.classList.add('debug-zoom-level');
-    marks.classList.add('debug-marks-amount');
-    userLat.innerHTML = `<b>${lang('lat')}</b> : -`;
-    userLng.innerHTML = `<b>${lang('lng')}</b> : -`;
-    updatesAmount.innerHTML = `<b>${lang('updates')}</b> : 0`;
-    userAccuracy.innerHTML = `<b>${lang('accuracy')}</b> : -`;
-    highAccuracy.innerHTML = `<b>${lang('highAccuracy')}</b> : -`;
-    maxAge.innerHTML = `<b>${lang('posAge')}</b> : -`;
-    posTimeout.innerHTML = `<b>${lang('posTimeout')}</b> : -`;
-    zoomLevel.innerHTML = `<b>${lang('zoom')}</b> : -`;
-    marks.innerHTML = `<b>${lang('marks')}</b> : -`;
-    debugContainer.appendChild(userLat);
-    debugContainer.appendChild(userLng);
-    debugContainer.appendChild(updatesAmount);
-    debugContainer.appendChild(userAccuracy);
-    debugContainer.appendChild(highAccuracy);
-    debugContainer.appendChild(maxAge);
-    debugContainer.appendChild(posTimeout);
-    debugContainer.appendChild(zoomLevel);
-    debugContainer.appendChild(marks);
-    return debugContainer;
-  }
-
-
-  static updateDebugInterface(element, user, options) {
-    if (window.DEBUG === true) {
-      const bc = window.BeerCrackerz;
-      const lang = bc.nls.debug.bind(bc.nls);
-      const updates = parseInt(element.querySelector('.debug-updates-amount').innerHTML.split(' : ')[1]) + 1;
-      const marks = bc.marks.spot.length + bc.marks.store.length + bc.marks.bar.length;
-      element.querySelector('.debug-user-lat').innerHTML = `
-        <b>${lang('lat')}</b> : ${user.lat}
-      `;
-      element.querySelector('.debug-user-lng').innerHTML = `
-        <b>${lang('lng')}</b> : ${user.lng}
-      `;
-      element.querySelector('.debug-updates-amount').innerHTML = `
-        <b>${lang('updates')}</b> : ${updates}
-      `;
-      element.querySelector('.debug-user-accuracy').innerHTML = `
-        <b>${lang('accuracy')}</b> : ${Utils.precisionRound(user.accuracy, 2)}m
-      `;
-      element.querySelector('.debug-high-accuracy').innerHTML = `
-        <b>${lang('highAccuracy')}</b> : ${options.enableHighAccuracy === true ? lang('enabled') : lang('disabled')}
-      `;
-      element.querySelector('.debug-pos-max-age').innerHTML = `
-        <b>${lang('posAge')}</b> : ${options.maximumAge / 1000}s
-      `;
-      element.querySelector('.debug-pos-timeout').innerHTML = `
-        <b>${lang('posTimeout')}</b> : ${options.timeout / 1000}s
-      `;
-      element.querySelector('.debug-zoom-level').innerHTML = `
-        <b>${lang('zoom')}</b> : ${bc.map.getZoom()}
-      `;
-      element.querySelector('.debug-marks-amount').innerHTML = `
-        <b>${lang('marks')}</b> : ${marks}
-      `;
-    }
-  }
-
-
-  static getPreference(pref) {
-    return localStorage.getItem(pref) || null;
-  }
-
-
-  static setPreference(pref, value) {
-    localStorage.setItem(pref, value);
-  }
-
-
-  static get RANGE_COLOR() {
-    return '#ffd87d';
-  }
-
-
-  static get USER_COLOR() {
-    return '#63fff5';
-  }
-
-
-  static get SPOT_COLOR() {
-    return '#26ad23';
-  }
-
-
-  static get STORE_COLOR() {
-    return '#247dc9';
-  }
-
-
-  static get BAR_COLOR() {
-    return '#ca2a3d';
-  }
-
-
-  static get CIRCLE_RADIUS() {
-    return 100;
-  }
-
-
-  static get NEW_MARKER_RANGE() {
-    return 200;
-  }
-
-
-  static get MAP_BOUNDS() {
-    return window.L.latLngBounds(
-      window.L.latLng(-89.98155760646617, -180),
-      window.L.latLng(89.99346179538875, 180)
-    );
-  }
-
-
-  static get HIGH_ACCURACY() {
-    return {
-      enableHighAccuracy: true, // More consuption, better position
-      maximumAge: 1000, // A position will last 1s maximum
-      timeout: 900, // A position is updated in 0.9s maximum
-    };
-  }
-
-
-  static get OPTIMIZED_ACCURACY() {
-    return {
-      enableHighAccuracy: false, // Less consuption
-      maximumAge: 30000, // A position will last 30s maximum
-      timeout: 29000 // A position is updated in 29s maximum
-    };
-  }
-
-
-  static get SUPPORTED_LANGUAGE() {
-    return ['en', 'fr', 'es'];
-  }
-
-}
-
-
-export default Utils;
-
-
-
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.10 on Thu Feb 10 2022 17:00:51 GMT+0100 (GMT+01:00) -
- - - - - diff --git a/doc/beercrackerz/0.0.1/js_utils_Utils.js.html b/doc/beercrackerz/0.0.1/js_utils_Utils.js.html deleted file mode 100644 index 28c676f..0000000 --- a/doc/beercrackerz/0.0.1/js_utils_Utils.js.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - JSDoc: Source: js/utils/Utils.js - - - - - - - - - - -
- -

Source: js/utils/Utils.js

- - - - - - -
-
-
class Utils {
-
-
-  constructor() { /* Not meant to be instantiated, all methods should be static */ }
-
-
-  static fetchTemplate(url) {
-    return new Promise((resolve, reject) => {
-			fetch(url).then(data => {
-        data.text().then(html => {
-          resolve(document.createRange().createContextualFragment(html));
-        }).catch(reject);
-			}).catch(reject);
-		});
-  }
-
-
-  static fetchFile(url) {
-    return new Promise((resolve, reject) => {
-			fetch(url).then(data => {
-        data.text().then(string => {
-          resolve(string);
-        }).catch(reject);
-			}).catch(reject);
-		});    
-  }
-
-
-  static stripDom(html){
-    let doc = new DOMParser().parseFromString(html, 'text/html');
-    return doc.body.textContent || '';
-  }
-
-
-  static replaceString(element, string, value) {
-    element.innerHTML = element.innerHTML.replace(string, value);
-  }
-
-
-  static getDistanceBetweenCoords(from, to) {
-    // return distance in meters
-    var lon1 = (from[1] * Math.PI) / 180,
-      lat1 = (from[0] * Math.PI) / 180,
-      lon2 = (to[1] * Math.PI) / 180,
-      lat2 = (to[0] * Math.PI) / 180;
-
-    var deltaLat = lat2 - lat1;
-    var deltaLon = lon2 - lon1;
-
-    var a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2);
-    var c = 2 * Math.asin(Math.sqrt(a));
-    var EARTH_RADIUS = 6371;
-    return c * EARTH_RADIUS * 1000;
-  }
-
-
-  /** @method
-   * @name precisionRound
-   * @public
-   * @memberof Utils
-   * @author Arthur Beaulieu
-   * @since September 2018
-   * @description Do a Math.round with a given precision (ie amount of integers after the coma)
-   * @param {nunmber} value - The value to precisely round
-   * @param {number} precision - The number of integers after the coma
-   * @return {number} - The rounded value */
-  static precisionRound(value, precision) {
-    const multiplier = Math.pow(10, precision || 0);
-    return Math.round(value * multiplier) / multiplier;
-  }
-
-
-  static initDebugInterface() {
-    const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls);
-    const debugContainer = document.createElement('DIV');
-    const userLat = document.createElement('P');
-    const userLng = document.createElement('P');
-    const updatesAmount = document.createElement('P');
-    const userAccuracy = document.createElement('P');
-    const highAccuracy = document.createElement('P');
-    const maxAge = document.createElement('P');
-    const posTimeout = document.createElement('P');
-    const zoomLevel = document.createElement('P');
-    const marks = document.createElement('P');
-    debugContainer.classList.add('debug-container');
-    userLat.classList.add('debug-user-lat');
-    userLng.classList.add('debug-user-lng');
-    updatesAmount.classList.add('debug-updates-amount');
-    userAccuracy.classList.add('debug-user-accuracy');
-    highAccuracy.classList.add('debug-high-accuracy');
-    maxAge.classList.add('debug-pos-max-age');
-    posTimeout.classList.add('debug-pos-timeout');
-    zoomLevel.classList.add('debug-zoom-level');
-    marks.classList.add('debug-marks-amount');
-    userLat.innerHTML = `<b>${lang('lat')}</b> : -`;
-    userLng.innerHTML = `<b>${lang('lng')}</b> : -`;
-    updatesAmount.innerHTML = `<b>${lang('updates')}</b> : 0`;
-    userAccuracy.innerHTML = `<b>${lang('accuracy')}</b> : -`;
-    highAccuracy.innerHTML = `<b>${lang('highAccuracy')}</b> : -`;
-    maxAge.innerHTML = `<b>${lang('posAge')}</b> : -`;
-    posTimeout.innerHTML = `<b>${lang('posTimeout')}</b> : -`;
-    zoomLevel.innerHTML = `<b>${lang('zoom')}</b> : -`;
-    marks.innerHTML = `<b>${lang('marks')}</b> : -`;
-    debugContainer.appendChild(userLat);
-    debugContainer.appendChild(userLng);
-    debugContainer.appendChild(updatesAmount);
-    debugContainer.appendChild(userAccuracy);
-    debugContainer.appendChild(highAccuracy);
-    debugContainer.appendChild(maxAge);
-    debugContainer.appendChild(posTimeout);
-    debugContainer.appendChild(zoomLevel);
-    debugContainer.appendChild(marks);
-    return debugContainer;
-  }
-
-
-  static updateDebugInterface(element, user, options) {
-    if (window.DEBUG === true) {
-      const bc = window.BeerCrackerz;
-      const lang = bc.nls.debug.bind(bc.nls);
-      const updates = parseInt(element.querySelector('.debug-updates-amount').innerHTML.split(' : ')[1]) + 1;
-      const marks = bc.marks.spot.length + bc.marks.store.length + bc.marks.bar.length;
-      element.querySelector('.debug-user-lat').innerHTML = `
-        <b>${lang('lat')}</b> : ${user.lat}
-      `;
-      element.querySelector('.debug-user-lng').innerHTML = `
-        <b>${lang('lng')}</b> : ${user.lng}
-      `;
-      element.querySelector('.debug-updates-amount').innerHTML = `
-        <b>${lang('updates')}</b> : ${updates}
-      `;
-      element.querySelector('.debug-user-accuracy').innerHTML = `
-        <b>${lang('accuracy')}</b> : ${Utils.precisionRound(user.accuracy, 2)}m
-      `;
-      element.querySelector('.debug-high-accuracy').innerHTML = `
-        <b>${lang('highAccuracy')}</b> : ${options.enableHighAccuracy === true ? lang('enabled') : lang('disabled')}
-      `;
-      element.querySelector('.debug-pos-max-age').innerHTML = `
-        <b>${lang('posAge')}</b> : ${options.maximumAge / 1000}s
-      `;
-      element.querySelector('.debug-pos-timeout').innerHTML = `
-        <b>${lang('posTimeout')}</b> : ${options.timeout / 1000}s
-      `;
-      element.querySelector('.debug-zoom-level').innerHTML = `
-        <b>${lang('zoom')}</b> : ${bc.map.getZoom()}
-      `;
-      element.querySelector('.debug-marks-amount').innerHTML = `
-        <b>${lang('marks')}</b> : ${marks}
-      `;
-    }
-  }
-
-
-  static getPreference(pref) {
-    return localStorage.getItem(pref) || null;
-  }
-
-
-  static setPreference(pref, value) {
-    localStorage.setItem(pref, value);
-  }
-
-
-  static get RANGE_COLOR() {
-    return '#ffd87d';
-  }
-
-
-  static get USER_COLOR() {
-    return '#63fff5';
-  }
-
-
-  static get SPOT_COLOR() {
-    return '#26ad23';
-  }
-
-
-  static get STORE_COLOR() {
-    return '#247dc9';
-  }
-
-
-  static get BAR_COLOR() {
-    return '#ca2a3d';
-  }
-
-
-  static get CIRCLE_RADIUS() {
-    return 100;
-  }
-
-
-  static get NEW_MARKER_RANGE() {
-    return 200;
-  }
-
-
-  static get MAP_BOUNDS() {
-    return window.L.latLngBounds(
-      window.L.latLng(-89.98155760646617, -180),
-      window.L.latLng(89.99346179538875, 180)
-    );
-  }
-
-
-  static get HIGH_ACCURACY() {
-    return {
-      enableHighAccuracy: true, // More consuption, better position
-      maximumAge: 1000, // A position will last 1s maximum
-      timeout: 900, // A position is updated in 0.9s maximum
-    };
-  }
-
-
-  static get OPTIMIZED_ACCURACY() {
-    return {
-      enableHighAccuracy: false, // Less consuption
-      maximumAge: 30000, // A position will last 30s maximum
-      timeout: 29000 // A position is updated in 29s maximum
-    };
-  }
-
-
-  static get SUPPORTED_LANGUAGE() {
-    return ['en', 'fr', 'es'];
-  }
-
-  
-}
-
-
-export default Utils;
-
-
-
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.10 on Fri Feb 11 2022 16:38:03 GMT+0100 (GMT+01:00) -
- - - - - diff --git a/doc/beercrackerz/0.0.1/module-BeerCrackerz.html b/doc/beercrackerz/0.0.1/module-BeerCrackerz.html deleted file mode 100644 index 3719235..0000000 --- a/doc/beercrackerz/0.0.1/module-BeerCrackerz.html +++ /dev/null @@ -1,339 +0,0 @@ - - - - - JSDoc: Module: BeerCrackerz - - - - - - - - - - -
- -

Module: BeerCrackerz

- - - - - - -
- -
- - - - - -
- -
-
- - -
-This component handles the whole BeerCrackerz app. It includes the map manipulation, -the geolocation API to update the user position and process any map events that are -relevant to an UX stand point. For more information, please consult the application -description page at https://about.beercrackerz.org/ -
- - - - - - - - - - - - - - - - - - - -
- - - - -
Since:
-
  • January 2022
- - - - - - - - - - - - - - - -
Author:
-
-
    -
  • Arthur Beaulieu
  • -
-
- - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - -
- - -

Extends

- - - - -
    -
  • MapHelper
  • -
- - - - - - - - - - - - - - - -

Members

- - - -

(private, inner) _map :object

- - - - -
-

The core Leaflet.js map

-
- - - -
Type:
-
    -
  • - -object - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - -

(private, inner) _user :object

- - - - -
-

The user object holds everything useful to ensure a proper session

-
- - - -
Type:
-
    -
  • - -object - - -
  • -
- - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.7 on Fri Jan 14 2022 10:42:37 GMT+0100 (GMT+01:00) -
- - - - - \ No newline at end of file diff --git a/doc/jsDoc.json b/doc/jsDoc.json deleted file mode 100644 index 99e16ae..0000000 --- a/doc/jsDoc.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "plugins": ["plugins/markdown"], - "recurseDepth": 10, - "source": { - "include": ["./"], - "exclude": ["./assets/dist/", "./doc/", "./node_modules/", "./webpack/"], - "includePattern": ".+\\.js(doc|x)?$", - "excludePattern": "(^|\\/|\\\\)_" - }, - "sourceType": "module", - "tags": { - "allowUnknownTags": true, - "dictionaries": ["jsdoc", "closure"] - }, - "templates": { - "name": "BeerCrackerz", - "footerText": "BeerCrackerz - Version 0.0.2", - "cleverLinks": false, - "monospaceLinks": false - }, - "opts": { - "destination": "doc/", - "readme": "README.md", - "package": "package.json", - "access": "all", - "recurse": true, - "verbose": true, - "private": true - } -} diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..e4c0ed0 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,66 @@ +version: "3.9" + +services: + nginx: + container_name: ${NGINX_NAME} + restart: always + build: + context: . + dockerfile: ./.conf/production/nginx/Dockerfile + ports: + - ${SERVER_PORT}:${SERVER_PORT} + environment: + SERVER_HOST: ${SERVER_HOST} + SERVER_PORT: ${SERVER_PORT} + BACKEND_PROXY: ${BACKEND_NAME}:${BACKEND_PORT} + volumes: + - bc_static_files:/vol/static + - bc_media:/vol/media + - bc_logs:/var/log/nginx/back + depends_on: + - backend + backend: + container_name: ${BACKEND_NAME} + restart: always + build: + context: . + dockerfile: ./.conf/production/back/Dockerfile + environment: + BACKEND_PORT: ${BACKEND_PORT} + SERVER_URL: ${SERVER_URL} + SECRET_KEY: ${BACKEND_SECRET_KEY} + DEBUG: ${BACKEND_DEBUG} + ALLOWED_HOSTS: ${BACKEND_ALLOWED_HOSTS} + CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS} + DB_HOST: ${DB_HOST} + DB_PORT: ${DB_PORT} + DB_NAME: ${DB_NAME} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + BACKEND_USE_EMAIL_FILE_SYSTEM: ${BACKEND_USE_EMAIL_FILE_SYSTEM} + MAILJET_API_KEY: ${MAILJET_API_KEY} + MAILJET_API_SECRET: ${MAILJET_API_SECRET} + volumes: + - bc_static_files:/vol/static + - bc_media:/vol/media + depends_on: + - db + db: + image: postgres:${DB_POSTGRES_VERSION} + container_name: ${DB_HOST} + restart: always + environment: + POSTGRES_DB: ${DB_NAME} + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - bc_db:/var/lib/postgresql/data +volumes: + bc_db: + name: bc_db + bc_static_files: + name: bc_static_files + bc_media: + name: bc_media + bc_logs: + name: bc_logs diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..979cd98 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,63 @@ +version: "3.9" + +services: + nginx: + container_name: ${NGINX_NAME} + build: + context: . + dockerfile: ./.conf/development/nginx/Dockerfile + ports: + - ${SERVER_HTTP_PORT}:${SERVER_HTTP_PORT} + environment: + SERVER_HTTP_PORT: ${SERVER_HTTP_PORT} + BACKEND_NAME: ${BACKEND_NAME} + BACKEND_PORT: ${BACKEND_PORT} + volumes: + - ./static:/vol/static + - bc_media:/vol/media + depends_on: + - backend + backend: + container_name: ${BACKEND_NAME} + build: + context: . + dockerfile: ./.conf/development/back/Dockerfile + environment: + BACKEND_PORT: ${BACKEND_PORT} + SERVER_URL: http://${SERVER_HOST}:${SERVER_HTTP_PORT} + SECRET_KEY: ${BACKEND_SECRET_KEY} + DEBUG: ${BACKEND_DEBUG} + ALLOWED_HOSTS: ${BACKEND_ALLOWED_HOSTS} + CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS} + DB_HOST: ${DB_HOST} + DB_PORT: ${DB_PORT} + DB_NAME: ${DB_NAME} + DB_USER: ${DB_USER} + DB_PASSWORD: ${DB_PASSWORD} + BACKEND_USE_EMAIL_FILE_SYSTEM: ${BACKEND_USE_EMAIL_FILE_SYSTEM} + MAILJET_API_KEY: ${MAILJET_API_KEY} + MAILJET_API_SECRET: ${MAILJET_API_SECRET} + volumes: + - ./back:/back + - bc_media:/vol/media + depends_on: + - db + db: + image: postgres:${DB_POSTGRES_VERSION} + container_name: ${DB_HOST} + environment: + POSTGRES_DB: ${DB_NAME} + POSTGRES_USER: ${DB_USER} + POSTGRES_PASSWORD: ${DB_PASSWORD} + volumes: + - bc_db:/var/lib/postgresql/data + adminer: + container_name: ${DB_ADMINER_NAME} + image: adminer + ports: + - ${DB_ADMINER_PORT}:${DB_ADMINER_CONTAINER_PORT} +volumes: + bc_db: + name: bc_db + bc_media: + name: bc_media diff --git a/doc/beercrackerz/0.0.1/BeerCrackerz.html b/front/doc/BeerCrackerz.html similarity index 78% rename from doc/beercrackerz/0.0.1/BeerCrackerz.html rename to front/doc/BeerCrackerz.html index d03115d..7fcbdfb 100644 --- a/doc/beercrackerz/0.0.1/BeerCrackerz.html +++ b/front/doc/BeerCrackerz.html @@ -110,7 +110,7 @@

new BeerC
Source:
@@ -144,17 +144,6 @@

new BeerC -

Extends

- - - - -
    -
  • MapHelper
  • -
- - - @@ -225,7 +214,7 @@
Type:
Source:
@@ -243,13 +232,13 @@
Type:
-

(private) _debugElement :Object

+

(private) _isZooming :Boolean

-

The debug DOM object

+

Flag to know if a zoom action is occuring on map

@@ -258,7 +247,7 @@
Type:
  • -Object +Boolean
  • @@ -297,7 +286,7 @@
    Type:
    Source:
    @@ -315,13 +304,13 @@
    Type:
    -

    (private) _isZooming :Boolean

    +

    (private) _kom :Object

    -

    Flag to know if a zoom action is occuring on map

    +

    The communication manager to process all server call

    @@ -330,7 +319,7 @@
    Type:
    • -Boolean +Object
    • @@ -369,7 +358,7 @@
      Type:
      Source:
      @@ -387,7 +376,7 @@
      Type:
      -

      (private) _lang :Boolean

      +

      (private) _lang :Object

      @@ -402,7 +391,7 @@
      Type:
      • -Boolean +Object
      • @@ -441,7 +430,7 @@
        Type:
        Source:
        @@ -513,7 +502,7 @@
        Type:
        Source:
        @@ -537,7 +526,7 @@

        (private) _mark
        -

        The stored marks for spots, stores and bars

        +

        The stored marks for spots, shops and bars

        @@ -585,7 +574,7 @@

        Type:
        Source:
        @@ -657,7 +646,7 @@
        Type:
        Source:
        @@ -729,7 +718,7 @@
        Type:
        Source:
        @@ -801,7 +790,7 @@
        Type:
        Source:
        @@ -873,7 +862,7 @@
        Type:
        Source:
        @@ -945,7 +934,79 @@
        Type:
        Source:
        + + + + + + + + + + + + + + + + +

        (private) debugElement :Object

        + + + + +
        +

        The debug DOM object

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        @@ -1055,7 +1116,7 @@
        Properties:
        Source:
        @@ -1126,7 +1187,7 @@
        Properties:
        -

        Leaflet.js marks that holds spot/store/bar marks as subkeys

        +

        Leaflet.js marks that holds spot/shop/bar marks as subkeys

        @@ -1165,7 +1226,7 @@
        Properties:
        Source:
        @@ -1275,7 +1336,7 @@
        Properties:
        Source:
        @@ -1385,7 +1446,7 @@
        Properties:
        Source:
        @@ -1479,7 +1540,7 @@

        (private, static) Source:
        @@ -1580,7 +1641,7 @@

        (private, static
        Source:
        @@ -1705,7 +1766,7 @@

        (private, s
        Source:
        @@ -1828,7 +1889,7 @@

        (private, static) <
        Source:
        @@ -1895,7 +1956,7 @@

        (private, stati
        -The _initEvents() method will initialize all saved marker into the map. +The _initEvents() method will initialize all saved marker into the map. Markers must be retrieved from server with a specific format to ensure it works
        @@ -1951,7 +2012,7 @@

        (private, stati
        Source:
        @@ -2075,7 +2136,7 @@

        (private, s
        Source:
        @@ -2198,7 +2259,7 @@

        (private, static)
        Source:
        @@ -2256,7 +2317,7 @@
        Returns:
        -

        (private, static) _markerSaved(options)

        +

        (static) addDebugUI()

        @@ -2265,8 +2326,7 @@

        (private, stati
        -The _markerSaved() method is the callback used when a marker is created and added -to the map. It is the last method of a new marker proccess. +The addDebugUI() method appends the debug DOM element to the document body
        @@ -2278,55 +2338,6 @@

        (private, stati -
        Parameters:
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        NameTypeDescription
        options - - -Object - - - -

        The new marker options

        - - @@ -2370,7 +2381,7 @@
        Parameters:
        Source:
        @@ -2406,7 +2417,7 @@
        Parameters:
        -

        (static) aboutModal()

        +

        (static) deleteMark(options)

        @@ -2415,8 +2426,7 @@

        (static) a
        -The aboutModal() method will request the about modal, which contains -information about BeerCrackerz, cookies/tracking policies and links +This method will delete a mark after prompting the user if he trully wants to
        @@ -2428,6 +2438,55 @@

        (static) a +

        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        options + + +Object + + + +

        The mark options to delete

        + + @@ -2438,7 +2497,7 @@

        (static) a
        Since:
        -
        • January 2022
        +
        • February 2022
        @@ -2471,7 +2530,7 @@

        (static) a
        Source:
        @@ -2507,7 +2566,7 @@

        (static) a -

        (static) addDebugUI()

        +

        (static) editMark(options)

        @@ -2516,7 +2575,7 @@

        (static) a
        -The addDebugUI() method appends the debug DOM element to the document body +This method will open a mark edition modal
        @@ -2528,6 +2587,55 @@

        (static) a +

        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        options + + +Object + + + +

        The mark options to edit

        + + @@ -2538,7 +2646,7 @@

        (static) a
        Since:
        -
        • January 2022
        +
        • February 2022
        @@ -2571,7 +2679,7 @@

        (static) a
        Source:
        @@ -2607,7 +2715,7 @@

        (static) a -

        (static) closeModal(event)

        +

        (static) formatMarker(mark) → {Object}

        @@ -2616,8 +2724,8 @@

        (static) c
        -The closeModal() method will close any opened modal if the click event is -targeted on the modal overlay or on close buttons +This method formats a mark so it can be parsed using JSON.parse +in order to be later stored in database.
        @@ -2654,455 +2762,7 @@

        Parameters:
        - event - - - - - -Event - - - - - - - - - -

        The click event

        - - - - - - - - - - - -
        - - - - -
        Since:
        -
        • January 2022
        - - - - - - - - - - - - - - - -
        Author:
        -
        -
          -
        • Arthur Beaulieu
        • -
        -
        - - - - - - - - - -
        Source:
        -
        - - - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - -

        (static) deleteMarker(options)

        - - - - - - -
        -
        -This method will delete a mark after prompting the user if he trully wants to -
        -
        - - - - - - - - - -
        Parameters:
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        NameTypeDescription
        options - - -Object - - - -

        The mark options to delete

        - - - - - - -
        - - - - -
        Since:
        -
        • February 2022
        - - - - - - - - - - - - - - - -
        Author:
        -
        -
          -
        • Arthur Beaulieu
        • -
        -
        - - - - - - - - - -
        Source:
        -
        - - - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - -

        (static) deleteMarkModal(cb)

        - - - - - - -
        -
        -The deleteMarkModal() method will request the mark delete modal, which prompts -the user a confirmation to actually delete the mark -
        -
        - - - - - - - - - -
        Parameters:
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        NameTypeDescription
        cb - - -function - - - -

        The function to callback with true or false depending on user's choice

        - - - - - - -
        - - - - -
        Since:
        -
        • February 2022
        - - - - - - - - - - - - - - - -
        Author:
        -
        -
          -
        • Arthur Beaulieu
        • -
        -
        - - - - - - - - - -
        Source:
        -
        - - - - - - - -
        - - - - - - - - - - - - - - - - - - - - - - - - - - -

        (static) editMarker(options)

        - - - - - - -
        -
        -This method will open a mark edition modal -
        -
        - - - - - - - - - -
        Parameters:
        - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -3169,7 +2829,7 @@
        Parameters:
        Source:
        @@ -3194,156 +2854,28 @@
        Parameters:
        - - - - +
        Returns:
        - - - - - -

        (static) formatSavedMarker(mark)

        - - - - - - -
        -
        -This method formats a mark returned from MapHelper so it can be parsed -using JSON.parse (in order to store it in local storage/database) -
        +
        +

        The formatted mark

        - - - - - - -
        Parameters:
        - - -
        NameTypeDescription
        optionsmark @@ -3118,7 +2778,7 @@
        Parameters:
        -

        The mark options to edit

        The mark options to format for server communication

        - - - - - - - - - - +
        +
        + Type +
        +
        - -
        - - - - - - - - - - - - - - - - - - - - - - -
        NameTypeDescription
        mark - - Object - -

        The mark options from internal this._marks[type]

        - - - - - - -
        - - - - -
        Since:
        -
        • February 2022
        - - - - - - - - - - - - - - - -
        Author:
        -
        -
          -
        • Arthur Beaulieu
        • -
        - - - - - - - - - -
        Source:
        -
        - - - - - - -
        - - - - - - - - - - - - - - + @@ -3355,7 +2887,7 @@
        Parameters:
        -

        (static) hidShowModal()

        +

        (static) hidShowMenu()

        @@ -3364,8 +2896,8 @@

        (static)
        -The hidShowModal() method will request the hide show modal, which all -toggles for map elements ; labels/circles/spots/stores/bars +The hidShowMenu() method will request the hide show modal, which all +toggles for map elements ; labels/circles/spots/shops/bars
        @@ -3420,7 +2952,7 @@

        (static) Source:
        @@ -3569,7 +3101,7 @@

        Parameters:
        Source:
        @@ -3669,7 +3201,7 @@

        (static) Source:
        @@ -3715,7 +3247,7 @@

        (static)
        The toggleCircle() method will, depending on user preference, display or not -the circles around the spots/stores/bars marks. This circle indicates the minimal +the circles around the spots/shops/bars marks. This circle indicates the minimal distance which allow the user to make updates on the mark information
        @@ -3771,7 +3303,7 @@

        (static) Source:
        @@ -3873,7 +3405,7 @@

        (static)
        Source:
        @@ -3920,7 +3452,7 @@

        (static) The toggleFocusLock() method will, depending on user preference, lock or unlock the map centering around the user marker at each position refresh. This way the user -can roam while the map is following its position. +can roam while the map is following its position. @@ -3975,7 +3507,7 @@

        (static) Source:
        @@ -4079,7 +3611,7 @@

        (static)
        Source:
        @@ -4125,7 +3657,7 @@

        (static)
        The toggleLabel() method will, depending on user preference, display or not -the labels attached to spots/stores/bars marks. This label is basically the +the labels attached to spots/shops/bars marks. This label is basically the mark name given by its creator.
        @@ -4181,7 +3713,7 @@

        (static)
        Source:
        @@ -4228,7 +3760,7 @@

        (static) The toggleMarkers() method will, depending on user preference, display or not a given mark type. This way, the user can fine tune what is displayed on the map. -A mark type in spots/stores/bars must be given as an argument +A mark type in spots/shops/bars must be given as an argument @@ -4332,7 +3864,7 @@
        Parameters:
        Source:
        @@ -4432,108 +3964,7 @@

        (static) Source:
        - - - - - - - -

        - - - - - - - - - - - - - - - - - - - - - - - - - - -

        (static) updateMarkerCirclesVisibility()

        - - - - - - -
        -
        -The updateMarkerCirclesVisibility() method will update the circle visibility for -all mark types (spots/stores/bars) and for the user marker -
        -
        - - - - - - - - - - - - - -
        - - - - -
        Since:
        -
        • January 2022
        - - - - - - - - - - - - - - - -
        Author:
        -
        -
          -
        • Arthur Beaulieu
        • -
        -
        - - - - - - - - - -
        Source:
        -
        @@ -4569,7 +4000,7 @@

        (static) userProfileModal()

        +

        (static) userProfile()

        @@ -4578,7 +4009,7 @@

        (static)
        -The userProfileModal() method will request the user modal, which contains +The userProfile() method will request the user modal, which contains the user preferences, and the user profile information
        @@ -4634,7 +4065,7 @@

        (static) Source:
        @@ -4680,13 +4111,13 @@

        (static)
        - Documentation generated by JSDoc 3.6.10 on Fri Feb 11 2022 16:38:03 GMT+0100 (GMT+01:00) + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time)
        diff --git a/front/doc/BeerCrackerz.js.html b/front/doc/BeerCrackerz.js.html new file mode 100644 index 0000000..f0a888b --- /dev/null +++ b/front/doc/BeerCrackerz.js.html @@ -0,0 +1,953 @@ + + + + + JSDoc: Source: BeerCrackerz.js + + + + + + + + + + +
        + +

        Source: BeerCrackerz.js

        + + + + + + +
        +
        +
        import './BeerCrackerz.scss';
        +import Kom from './js/core/Kom.js';
        +import LangManager from './js/core/LangManager.js';
        +
        +import ZoomSlider from './js/ui/component/ZoomSlider.js';
        +import Notification from './js/ui/component/Notification.js';
        +import VisuHelper from './js/ui/VisuHelper.js';
        +import MarkPopup from './js/ui/MarkPopup';
        +import ModalFactory from './js/ui/ModalFactory';
        +
        +import CustomEvents from './js/utils/CustomEvents.js';
        +import Utils from './js/utils/Utils.js';
        +import AccuracyEnum from './js/utils/enums/AccuracyEnum.js';
        +import ClustersEnum from './js/utils/enums/ClusterEnum.js';
        +import ColorEnum from './js/utils/enums/ColorEnum.js';
        +import ProvidersEnum from './js/utils/enums/ProviderEnum.js';
        +import MapEnum from './js/utils/enums/MapEnum.js';
        +
        +
        +window.VERSION = '0.0.2';
        +window.Evts = new CustomEvents();
        +
        +
        +/**
        + * @class
        + * @constructor
        + * @public
        +**/
        +class BeerCrackerz {
        +
        +
        +  /**
        +   * @summary The BeerCrackerz main component
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * This component handles the whole BeerCrackerz app. It includes the map manipulation,
        +   * the geolocation API to update the user position and process any map events that are
        +   * relevant to an UX stand point. For more information, please consult the application
        +   * description page at <a href="https://about.beercrackerz.org">https://about.beercrackerz.org/</a>
        +   * </blockquote>
        +   **/
        +  constructor() {
        +    /**
        +     * The core Leaflet.js map
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._map = null;
        +    /**
        +     * The zoom slider handler
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._zoomSlider = null;
        +    /**
        +     * The notification handler
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._notification = null;
        +    /**
        +     * The user object holds everything useful to ensure a proper session
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._user = {
        +      lat: 48.853121540141096, // Default lat to Paris Notre-Dame latitude
        +      lng: 2.3498955769881156, // Default lng to Paris Notre-Dame longitude
        +      accuracy: 0, // Accuracy in meter given by geolocation API
        +      marker: null, // The user marker on map
        +      circle: null, // The accuracy circle around the user marker
        +      range: null, // The range in which user can add a new marker
        +      color: ColorEnum.user, // The color to use for circle (match the user marker color)
        +      id: -1,
        +      username: ''
        +    };
        +    /**
        +     * The stored marks for spots, shops and bars
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._marks = {
        +      spot: [],
        +      shop: [],
        +      bar: []
        +    };
        +    /**
        +     * The stored clusters for markers, see Leaflet.markercluster plugin
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._clusters = {
        +      spot: {},
        +      shop: {},
        +      bar: {}
        +    };
        +    /**
        +     * The temporary marker for new marks only
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._newMarker = null;
        +    /**
        +     * The debug DOM object
        +     * @type {Object}
        +     * @private
        +     **/
        +    this.debugElement = null;
        +    /**
        +     * ID for geolocation watch callback
        +     * @type {Number}
        +     * @private
        +     **/
        +    this._watchId = null;
        +    /**
        +     * Flag to know if a zoom action is occuring on map
        +     * @type {Boolean}
        +     * @private
        +     **/
        +    this._isZooming = false;
        +    /**
        +     * The communication manager to process all server call
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._kom = new Kom();
        +    /**
        +     * The LangManager must be instantiated to handle nls accross the app
        +     * @type {Object}
        +     * @private
        +     **/
        +    // The BeerCrackerz app is only initialized once nls are set up
        +    this._lang = new LangManager();
        +    // Start app initialization
        +    this._init();
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ----------------- Application initialization sequence ------------------ //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name _init
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _init() method is designed to properly configure the user session, according
        +   * to its saved preferences and its position. It first build the debug interface,
        +   * then loads the user preferences, then create the map and finally, events are listened.
        +   * </blockquote>
        +   **/
        +  _init() {
        +    this._notification = new Notification();
        +    this._initUser()
        +      .then(this._initPreferences.bind(this))
        +      .then(this._initGeolocation.bind(this))
        +      .then(this._initMap.bind(this))
        +      .then(this._initMarkers.bind(this))
        +      .then(this._initEvents.bind(this))
        +      .then(this._startSession.bind(this));
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _initUser
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _init() method initialize the user object according to its information
        +   * and statistic so the UI can be properly built.
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initUser() {
        +    return new Promise((resolve, reject) => {
        +      this._kom.get('/api/user/me/').then(user => {
        +        this._user.id = user.id;
        +        this._user.username = user.username;
        +        this._user.email = user.email;
        +        this._user.pp = user.profilePicture;
        +        this._user.isActive = user.isActive;
        +        resolve();
        +      }).catch(reject);
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _initPreferences
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _initPreferences() will initialize user preference if they are not set yet,
        +   * it will also update the UI according to user preferences ; debug DOM visible,
        +   * update the command classList for selected ones.
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initPreferences() {
        +    return new Promise((resolve, reject) => {
        +      // If no pref, set fallbacks
        +      Utils.setDefaultPreferences();
        +      // Update icon class if center on preference is set to true
        +      if (Utils.getPreference('map-center-on-user') === 'true') {
        +        document.getElementById('center-on').classList.add('lock-center-on');
        +      }
        +      // Replace dark-theme class with light-theme class on body
        +      if (Utils.getPreference('dark-theme') === 'false') {
        +        document.body.classList.remove('dark-theme');
        +        document.body.classList.add('light-theme');
        +      }
        +      // Update LangManager with pref language
        +      this.nls.updateLang(Utils.getPreference('selected-lang')).then(() => {
        +        this.debugElement = VisuHelper.initDebugUI();
        +        // Create and append debug UI with proper nls settings
        +        if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) {
        +          window.DEBUG = true; // Ensure to set global flag if preference comes from local storage
        +          Utils.setPreference('app-debug', true); // Ensure to set local storage preference if debug flag was added to the url
        +          VisuHelper.addDebugUI();
        +        }
        +        resolve();
        +      }).catch(reject);
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _initGeolocation
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _initGeolocation() method will request from browser the location authorization.
        +   * Once granted, an event listener is set on any position update, so it can update the
        +   * map state and the markers position. This method can be called again, only if the
        +   * geolocation watch has been cleared ; for example when updating the accuracy options.
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initGeolocation() {
        +    return new Promise(resolve => {
        +      if ('geolocation' in navigator) {
        +        const options = (Utils.getPreference('map-high-accuracy') === 'true') ? AccuracyEnum.high : AccuracyEnum.optimized;
        +        this._watchId = navigator.geolocation.watchPosition(position => {
        +          // Update saved user position
        +          this._user.lat = position.coords.latitude;
        +          this._user.lng = position.coords.longitude;
        +          this._user.accuracy = position.coords.accuracy;
        +          // Only draw marker if map is already created
        +          if (this._map) {
        +            VisuHelper.drawUserMarker();
        +            VisuHelper.updateMarkerCirclesVisibility();
        +            // Update map position if focus lock is active
        +            if (Utils.getPreference('map-center-on-user') === 'true' && !this._isZooming) {
        +              this._map.setView(this._user);
        +            }
        +            // Updating debug info
        +            VisuHelper.updateDebugUI();
        +          }
        +          resolve();
        +        }, resolve, options);
        +      } else {
        +        this.notification.raise(this.nls.notif('geolocationError'));
        +        resolve();
        +      }
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _initMap
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite),
        +   * add scale control, remove zoom control and set map bounds.
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initMap() {
        +    return new Promise(resolve => {
        +      // Use main div to inject OSM into
        +      this._map = window.L.map('beer-crakerz-map', {
        +        zoomControl: false,
        +      }).setView([this._user.lat, this._user.lng], 18);
        +      // Add meter and feet scale on map
        +      window.L.control.scale().addTo(this._map);
        +      // Place user marker on the map
        +      VisuHelper.drawUserMarker();
        +      // Add OSM credits to the map next to leaflet credits
        +      const osm = ProvidersEnum.planOsm;
        +      const esri = ProvidersEnum.satEsri;
        +      // Prevent panning outside of the world's edge
        +      this._map.setMaxBounds(MapEnum.mapBounds);
        +      // Add layer group to interface
        +      const baseMaps = {};
        +      baseMaps[`<p>${this.nls.map('planLayerOSM')}</p>`] = osm;
        +      baseMaps[`<p>${this.nls.map('satLayerEsri')}</p>`] = esri;
        +      // Append layer depending on user preference
        +      if (Utils.getPreference('map-plan-layer')) {
        +        switch (Utils.getPreference('map-plan-layer')) {
        +          case this.nls.map('planLayerOSM'):
        +            osm.addTo(this._map);
        +            break;
        +          case this.nls.map('satLayerEsri'):
        +            esri.addTo(this._map);
        +            break;
        +          default:
        +            osm.addTo(this._map);
        +            break;
        +        }
        +      } else { // No saved pref, fallback on OSM base map
        +        osm.addTo(this._map);
        +      }
        +      // Add layer switch radio on bottom right of the map
        +      window.L.control.layers(baseMaps, {}, { position: 'bottomright' }).addTo(this._map);
        +      // Init zoom slider when map has been created
        +      this._zoomSlider = new ZoomSlider(this._map);
        +      resolve();
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _initMarkers
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _initEvents() method will initialize all saved marker into the map.
        +   * Markers must be retrieved from server with a specific format to ensure it works
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initMarkers() {
        +    return new Promise(resolve => {
        +      // Init map clusters for marks to be displayed (disable clustering at opened popup zoom level)
        +      this._clusters.spot = ClustersEnum.spot;
        +      this._clusters.shop = ClustersEnum.shop;
        +      this._clusters.bar = ClustersEnum.bar;
        +      // Append clusters to the map depending on user preferences
        +      if (Utils.getPreference(`poi-show-spot`) === 'true') {
        +        this._map.addLayer(this._clusters.spot);
        +      }
        +      if (Utils.getPreference(`poi-show-shop`) === 'true') {
        +        this._map.addLayer(this._clusters.shop);
        +      }
        +      if (Utils.getPreference(`poi-show-bar`) === 'true') {
        +        this._map.addLayer(this._clusters.bar);
        +      }
        +      // Load data from local storage, later to be fetched from server
        +      const iterateMarkers = mark => {
        +        const popup = new MarkPopup(mark, dom => {
        +          mark.dom = dom;
        +          mark.marker = VisuHelper.addMark(mark);
        +          mark.popup = popup;
        +          this._marks[mark.type].push(mark);
        +          this._clusters[mark.type].addLayer(mark.marker);
        +        });
        +      };
        +
        +      this._kom.getSpots().then(spots => {
        +        for (let i = 0; i < spots.length; ++i) {
        +          iterateMarkers(spots[i]);
        +        }
        +      });
        +
        +      this._kom.getShops().then(shops => {
        +        for (let i = 0; i < shops.length; ++i) {
        +          iterateMarkers(shops[i]);
        +        }
        +      });
        +
        +      this._kom.getBars().then(bars => {
        +        for (let i = 0; i < bars.length; ++i) {
        +          iterateMarkers(bars[i]);
        +        }
        +      });
        +
        +      VisuHelper.updateMarkerCirclesVisibility();
        +      resolve();
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _initEvents
        +   * @private
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The _initEvents() method will listen to all required events to manipulate the map. Those events
        +   * are both for commands and for map events (click, drag, zoom and layer change).
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +   _initEvents() {
        +    return new Promise(resolve => {
        +      // Subscribe to click event on map to react
        +      this._map.on('click', this.mapClicked.bind(this));
        +      // Map is dragged by user mouse/finger
        +      this._map.on('drag', () => {
        +        // Constrain pan to the map bounds
        +        this._map.panInsideBounds(MapEnum.mapBounds, { animate: true });
        +        // Disable lock focus if user drags the map
        +        if (Utils.getPreference('map-center-on-user') === 'true') {
        +          VisuHelper.toggleFocusLock();
        +        }
        +      });
        +      // Map events
        +      this._map.on('zoomstart', () => {
        +        this._isZooming = true;
        +        if (Utils.getPreference('poi-show-circle') === 'true') {
        +          VisuHelper.setMarkerCircles(false);
        +        }
        +      });
        +      this._map.on('zoomend', () => {
        +        this._isZooming = false;
        +        if (Utils.getPreference('poi-show-circle') === 'true') {
        +          if (this._map.getZoom() >= 10) {
        +            VisuHelper.setMarkerCircles(true);
        +          }
        +        }
        +        // Auto hide labels if zoom level is too high (and restore it when needed)
        +        if (Utils.getPreference('poi-show-label') === 'true') {
        +          if (this._map.getZoom() < 16) {
        +            VisuHelper.setMarkerLabels(false);
        +          } else {
        +            VisuHelper.setMarkerLabels(true);
        +          }
        +        }
        +        // Updating debug info
        +        VisuHelper.updateDebugUI();
        +      });
        +      this._map.on('baselayerchange', event => {
        +        Utils.setPreference('map-plan-layer', Utils.stripDom(event.name));
        +      });
        +      // Clustering events
        +      this._clusters.spot.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'spot'));
        +      this._clusters.shop.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'shop'));
        +      this._clusters.bar.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'bar'));
        +      // Command events
        +      window.Evts.addEvent('click', document.getElementById('user-profile'), this.userProfile, this);
        +      window.Evts.addEvent('click', document.getElementById('hide-show'), this.hidShowMenu, this);
        +      window.Evts.addEvent('click', document.getElementById('center-on'), VisuHelper.toggleFocusLock, this);
        +      // Bus events
        +      window.Evts.subscribe('addMark', this.addMark.bind(this)); // Event from addMarkPopup
        +      window.Evts.subscribe('onMarkAdded', this._onMarkAdded.bind(this)); // Event from MarkPopup
        +      window.Evts.subscribe('deleteMark', this.deleteMark.bind(this)); // Event from MarkPopup
        +      window.Evts.subscribe('onMarkDeleted', this._onMarkDeleted.bind(this)); // User confirmed the mark deletion
        +      window.Evts.subscribe('editMark', this.editMark.bind(this)); // Event from MarkPopup
        +      window.Evts.subscribe('onMarkEdited', this._onMarkEdited.bind(this)); // User confirmed the mark edition
        +      window.Evts.subscribe('updateProfile', this.updateProfilePicture.bind(this)); // Event from user modal
        +      window.Evts.subscribe('onProfilePictureUpdated', this._onProfilePictureUpdated.bind(this)); // Event from update pp modal
        +
        +      window.Evts.subscribe('centerOn', VisuHelper.centerOn.bind(VisuHelper));
        +
        +      resolve();
        +    });
        +  }
        +
        +
        +  _startSession() {
        +    if (Utils.getPreference(`startup-help`) === 'true') {
        +      this._modal = ModalFactory.build('StartupHelp');
        +    } else {
        +      this.notification.raise(this.nls.notif('welcomeBack'));      
        +    }
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ------------------------- Toggle for map items ------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name toggleHighAccuracy
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The toggleHighAccuracy() method will, depending on user preference, update the
        +   * geolocation accuracy between optimized and high. The high settings might cause
        +   * more memory and processing consumption, but gives better results. It will clear
        +   * any previous position watch on the geolocation API so it can subscribe a new one
        +   * with the new accuracy parameters (see Utils for values)
        +   * </blockquote>
        +   **/
        +  toggleHighAccuracy() {
        +    const high = !(Utils.getPreference('map-high-accuracy') === 'true');
        +    Utils.setPreference('map-high-accuracy', high);
        +    navigator.geolocation.clearWatch(this._watchId);
        +    this._initGeolocation().then(VisuHelper.updateDebugUI.bind(VisuHelper));
        +  }
        +
        +
        +  updateLang(event) {
        +    Utils.setPreference('selected-lang', event.target.value);
        +    this.nls.updateLang(Utils.getPreference('selected-lang')).then(() => {
        +      if (this._modal) {
        +        this._modal.close(null, true);
        +        VisuHelper.updateDebugUI();
        +        setTimeout(this.userProfile.bind(this), 200);
        +      } else { // Lang update from elsewhere than user modal
        +        window.location.reload();
        +      }
        +    });
        +  }
        +
        +
        +  // ======================================================================== //
        +  // -------------------------- Map interaction ----------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name mapClicked
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The mapClicked() method is the callback used when the user clicked on the Leaflet.js map
        +   * </blockquote>
        +   * @param {Event} event The click event
        +   **/
        +  mapClicked(event) {
        +    if (this._newMarker && this._newMarker.popupClosed) {
        +      // Avoid to open new marker right after popup closing
        +      this._newMarker = null;
        +    } else if (this._newMarker === null || !this._newMarker.isBeingDefined) {
        +      // Only create new marker if none is in progress, and that click is max range to add a marker
        +      const distance = Utils.getDistanceBetweenCoords([this._user.lat, this._user.lng], [event.latlng.lat, event.latlng.lng]);
        +      if (distance < MapEnum.newMarkRange) {
        +        this.addMarkPopup(event.latlng);
        +      } else if (this._map.getZoom() >= 10) {
        +        this.notification.raise(this.nls.notif('newMarkerOutside'));
        +      }
        +    }
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ----------------------- Marker manipulation ---------------------------- //
        +  // ======================================================================== //
        +
        +
        +  addMarkPopup(options) {
        +    const dom = {
        +      wrapper: document.createElement('DIV'),
        +      title: document.createElement('P'),
        +      spot: document.createElement('BUTTON'),
        +      shop: document.createElement('BUTTON'),
        +      bar: document.createElement('BUTTON'),
        +    };
        +    // Update class and inner HTMl content according to user's nls
        +    dom.wrapper.className = 'new-poi';
        +    dom.title.innerHTML = this.nls.map('newTitle');
        +    dom.spot.innerHTML = this.nls.map('newSpot');
        +    dom.shop.innerHTML = this.nls.map('newShop');
        +    dom.bar.innerHTML = this.nls.map('newBar');
        +    // Atach data type to each button (to be used in clicked callback)
        +    dom.spot.dataset.type = 'spot';
        +    dom.shop.dataset.type = 'shop';
        +    dom.bar.dataset.type = 'bar';
        +    // DOM chaining
        +    dom.wrapper.appendChild(dom.title);
        +    dom.wrapper.appendChild(dom.spot);
        +    dom.wrapper.appendChild(dom.shop);
        +    dom.wrapper.appendChild(dom.bar);
        +    // Update popup content with DOM elements
        +    options.dom = dom.wrapper;
        +    // Create temporary mark with wrapper content and open it to offer user the creation menu
        +    this._newMarker = VisuHelper.addMark(options).openPopup();
        +    options.marker = this._newMarker; // Attach marker to option so it can be manipulated in clicked callbacks
        +    // Callback on button clicked (to open modal and define a new mark)
        +    const _prepareNewMark = e => {
        +      this._newMarker.isBeingDefined = true;
        +      this._newMarker.closePopup();
        +      options.type = e.target.dataset.type;
        +      window.Evts.publish('addMark', options);
        +    };
        +    // Buttons click events
        +    dom.spot.addEventListener('click', _prepareNewMark);
        +    dom.shop.addEventListener('click', _prepareNewMark);
        +    dom.bar.addEventListener('click', _prepareNewMark);
        +    // Listen to clicks outside of popup to close new mark
        +    this._newMarker.on('popupclose', () => {
        +      if (!this._newMarker.isBeingDefined) {
        +        this._newMarker.popupClosed = true;
        +        this._newMarker.removeFrom(this.map);
        +      }
        +    });
        +  }
        +
        +
        +  addMark(options) {
        +    this._modal = ModalFactory.build('AddMark', options);
        +  }
        +
        +
        +  _onMarkAdded(options) {
        +    const popup = new MarkPopup(options, dom => {
        +      options.dom = dom;
        +      options.marker = VisuHelper.addMark(options); // Create final marker
        +      options.popup = popup;
        +      // Save new marker in local storage, later to be sent to the server
        +      this._kom[`${options.type}Created`](Utils.formatMarker(options)).then(data => {
        +        // Update marker data to session memory
        +        options.id = data.id;
        +        options.name = data.name;
        +        options.description = data.description;
        +        options.lat = data.lat;
        +        options.lng = data.lng;
        +        // Save marke in marks and clusters for the map
        +        this._marks[options.type].push(options);
        +        this._clusters[options.type].addLayer(options.marker);
        +        // Notify user that new marker has been saved
        +        this.notification.raise(this.nls.notif(`${options.type}Added`));
        +        // Update marker circles visibility according to user position
        +        VisuHelper.updateMarkerCirclesVisibility();
        +        // Clear new marker to let user add other stuff
        +        this._newMarker = null;
        +      }).catch(() => {
        +        this.notification.raise(this.nls.notif(`${options.type}NotAdded`));
        +      });
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name deleteMark
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since February 2022
        +   * @description
        +   * <blockquote>
        +   * This method will delete a mark after prompting the user if he trully wants to
        +   * </blockquote>
        +   * @param {Object} options The mark options to delete
        +   **/
        +  deleteMark(options) {
        +    this._map.closePopup();
        +    this._modal = ModalFactory.build('DeleteMark', options);
        +  }
        +
        +
        +  _onMarkDeleted(options) {
        +    // Iterate through marks to find matching one (by coord as marks coordinates are unique)
        +    const marks = this._marks[options.type];
        +    for (let i = 0; i < marks.length; ++i) {
        +      // We found, remove circle, label and marker from map/clusters
        +      if (options.lat === marks[i].lat && options.lng === marks[i].lng) {
        +        // Send data to server
        +        this._kom[`${options.type}Deleted`](marks[i].id, Utils.formatMarker(marks[i])).then(() => {
        +          VisuHelper.removeMarkDecoration(marks[i]);
        +          this._clusters[options.type].removeLayer(marks[i].marker);
        +          marks.splice(i, 1);
        +          // Update internal marks array
        +          this._marks[options.type] = marks;
        +          // Notify user through UI that marker has been successfully deleted
        +          this.notification.raise(this.nls.notif(`${options.type}Deleted`));
        +        }).catch(() => {
        +          this.notification.raise(this.nls.notif(`${options.type}NotDeleted`));
        +        });
        +        break;
        +      }
        +    }
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name editMark
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since February 2022
        +   * @description
        +   * <blockquote>
        +   * This method will open a mark edition modal
        +   * </blockquote>
        +   * @param {Object} options The mark options to edit
        +   **/
        +   editMark(options) {
        +    this._map.closePopup();
        +    this._modal = ModalFactory.build('EditMark', options);
        +  }
        +
        +
        +  _onMarkEdited(options) {
        +    // Iterate through marks to find matching one (by coord as marks coordinates are unique)
        +    for (let i = 0; i < this._marks[options.type].length; ++i) {
        +      // We found, remove circle, label and marker from map/clusters
        +      if (options.lat === this._marks[options.type][i].lat && options.lng === this._marks[options.type][i].lng) {
        +        this._marks[options.type][i].name = options.name.value;
        +        this._marks[options.type][i].description = options.description.value;
        +        this._marks[options.type][i].rate = options.rating;
        +        if (options.price) {
        +          this._marks[options.type][i].price = options.price;
        +        }
        +        options.tooltip.removeFrom(this.map);
        +        const popup = new MarkPopup(options, dom => {
        +          options.dom = dom;
        +          options.marker.setPopupContent(popup.dom);
        +          options.popup = popup;
        +          // Send data to server
        +          this._kom[`${options.type}Edited`](options.id, Utils.formatMarker(options)).then(data => {
        +            // Update marker data to session memory
        +            options.id = data.id;
        +            options.name = data.name;
        +            options.description = data.description;
        +            options.lat = data.lat;
        +            options.lng = data.lng;
        +            // Notify user through UI that marker has been successfully deleted
        +            this.notification.raise(this.nls.notif(`${options.type}Edited`));
        +          }).catch(() => {
        +            this.notification.raise(this.nls.notif(`${options.type}NotEdited`));
        +          }).finally(() => {
        +            this._modal.close(null, true);
        +          });
        +        });
        +        break;
        +      }
        +    }
        +  }
        +
        +
        +  // ======================================================================== //
        +  // --------------------------- User profile ------------------------------- //
        +  // ======================================================================== //
        +
        +
        +
        +  /**
        +   * @method
        +   * @name userProfile
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The userProfile() method will request the user modal, which contains
        +   * the user preferences, and the user profile information
        +   * </blockquote>
        +   **/
        +  userProfile() {
        +    this._modal = ModalFactory.build('User');
        +  }
        +
        +
        +  updateProfilePicture(options) {
        +    this._modal.close(null, true);
        +    setTimeout(() => {
        +      this._modal = ModalFactory.build('UpdateProfilePicture', options);
        +    }, 200);
        +  }
        +
        +
        +  _onProfilePictureUpdated(options) {
        +    this._kom.patchImage(`api/user/${this._user.id}/profile-picture/`, {
        +      profile_picture: document.getElementById('wip-pp').src,
        +      minX: Math.round(options.imageResizer.getMinPoint().x),
        +      minY: Math.round(options.imageResizer.getMinPoint().y),
        +      maxX: Math.round(options.imageResizer.getMaxPoint().x),
        +      maxY: Math.round(options.imageResizer.getMaxPoint().y)
        +    }).then(() => {
        +      this.notification.raise(this.nls.notif('uploadPPSuccess'));
        +    }).catch(() => {
        +      this.notification.raise(this.nls.notif('uploadPPFailed'));
        +    }).finally(() => {
        +      this._modal.close(null, true);
        +      // Reload user from server with new path and close modal
        +      this._initUser().then(() => {
        +        setTimeout(() => {
        +          this.userProfile();
        +        }, 200);
        +      });
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name hidShowMenu
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The hidShowMenu() method will request the hide show modal, which all
        +   * toggles for map elements ; labels/circles/spots/shops/bars
        +   * </blockquote>
        +   **/
        +   hidShowMenu() {
        +    this._modal = ModalFactory.build('HideShow');
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ---------------------------- Class accessors --------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} map
        +   * Leaflet.js map getter
        +   **/
        +  get map() {
        +    return this._map;
        +  }
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} marks
        +   * Leaflet.js marks that holds spot/shop/bar marks as subkeys
        +   **/
        +  get marks() {
        +    return this._marks;
        +  }
        +
        +
        +  get clusters() {
        +    return this._clusters;
        +  }
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} user
        +   * The session user object
        +   **/
        +  get user() {
        +    return this._user;
        +  }
        +
        +
        +  get kom() {
        +    return this._kom;
        +  }
        +
        +
        +  get notification() {
        +    return this._notification;
        +  }
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} nls
        +   * The LangManager getter
        +   **/
        +  get nls() {
        +    return this._lang;
        +  }
        +
        +
        +}
        +
        +
        +export default BeerCrackerz;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/BeerCrackerzAuth.html b/front/doc/BeerCrackerzAuth.html new file mode 100644 index 0000000..a5b93c2 --- /dev/null +++ b/front/doc/BeerCrackerzAuth.html @@ -0,0 +1,3861 @@ + + + + + JSDoc: Class: BeerCrackerzAuth + + + + + + + + + + +
        + +

        Class: BeerCrackerzAuth

        + + + + + + +
        + +
        + +

        BeerCrackerzAuth()

        + + +
        + +
        +
        + + + + + + +

        new BeerCrackerzAuth()

        + + + +

        The BeerCrackerzAuth main component

        + + + + +
        +
        +This component handles all the authentication pages for BeerCrackerz. It provides the login, the +register and the forgot password process. It also provides a public map so unauthenticated user +can still browse the best BeerCrackerz spots. For more information, please consult the application +description page at https://about.beercrackerz.org/ +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + +

        Members

        + + + +

        (private) _aside :Object

        + + + + +
        +

        The Aside DOM container

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _clusters :Object

        + + + + +
        +

        The stored clusters for markers, see Leaflet.markercluster plugin

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _isAsideExpanded :Boolean

        + + + + +
        +

        The Aside expand status

        +
        + + + +
        Type:
        +
          +
        • + +Boolean + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _kom :Object

        + + + + +
        +

        The server communication class

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _lang :Object

        + + + + +
        +

        The frontend i18n manager

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _marks :Object

        + + + + +
        +

        The stored marks for spots, shops and bars

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _user :Object

        + + + + +
        +

        The minimal user object holds position and accuracy

        +
        + + + +
        Type:
        +
          +
        • + +Object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        map

        + + + + + + + + + + +
        Properties:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        map + + +Object + + + +

        Leaflet.js map getter

        + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        marks

        + + + + + + + + + + +
        Properties:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        marks + + +Object + + + +

        Leaflet.js marks that holds spot/shop/bar marks as subkeys

        + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        nls

        + + + + + + + + + + +
        Properties:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        nls + + +Object + + + +

        The LangManager getter

        + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        user

        + + + + + + + + + + +
        Properties:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        user + + +Object + + + +

        The session user object

        + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + +

        Methods

        + + + + + + + +

        (private, static) _createMarker(options) → {HTMLElement}

        + + + + + + +
        +
        +The _createMarker() method will create all BeerCrackerz kind of markers (spot/shop/bar/user), +will create if needed its popup (if provided in options) and will make it interactive to click. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        options + + +Object + + + +

        The marker options

        +
        Properties
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeAttributesDescription
        type + + +String + + + + + + + + + +

        The marker type in spot/shop/bar/user

        lat + + +Float + + + + + + + + + +

        The marker latitude

        lng + + +Float + + + + + + + + + +

        The marker longitude

        dom + + +HTMLElement + + + + + + <optional>
        + + + + + +

        The marker popup content

        + +
        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The Leaflet marker extended with option properties

        +
        + + + +
        +
        + Type +
        +
        + +HTMLElement + + +
        +
        + + + + + + + + + + + + + +

        (private, static) _drawUserMarker()

        + + + + + + +
        +
        +The _drawUserMarker() method will draw the user marker to the position received +from the geolocation API. If the marker doesn't exist yet, it will create it and +place it to its default position (see constructor/this._user). +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _fatalError(err)

        + + + + + + +
        +
        +The _fatalError() method will handle all fatal errors from which the app +can't recover. It redirects to the error page and send info through the referrer +so the error page can properly displays it to the user +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        err + + +Object + + + +

        The error object with its info

        +
        Properties
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeAttributesDescription
        status + + +Number + + + + + + <optional>
        + + + + + +

        The HTTP error code

        url + + +String + + + + + + <optional>
        + + + + + +

        The URL that generated the HTTP error

        file + + +String + + + + + + <optional>
        + + + + + +

        The file in which the fatal error happened

        msg + + +String + + + + + + <optional>
        + + + + + +

        The custom error message

        + +
        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _handleForgotPasswordAside()

        + + + + + + +
        +
        +The _handleForgotPasswordAside() method will replace the aside content with the fogot password +template, then it will handle its i18n, and all of its interactivity to submit forgot password +form to the server. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _handleLoginAside()

        + + + + + + +
        +
        +The _handleLoginAside() method will replace the aside content with the login template, +then it will handle its i18n, and all of its interactivity to submit login form to the server. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _handleRegisterAside()

        + + + + + + +
        +
        +The _handleRegisterAside() method will replace the aside content with the register template, +then it will handle its i18n, and all of its interactivity to submit register form to the server. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _init()

        + + + + + + +
        +
        +The _init() method handle the whole app initialization sequence. It first +set the aside content to login (as it comes with the base welcome.html template), +then initialize the communication and notification handler, and will finally +initialize the whole map, markers and interactivity. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (async, private, static) _initEvents() → {Promise}

        + + + + + + +
        +
        +The _initEvents() method will listen to all required events to manipulate the map. Those events +are both for commands and for map events (click, drag, zoom and layer change). +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A Promise resolved when preferences are set

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _initGeolocation() → {Promise}

        + + + + + + +
        +
        +The _initGeolocation() method will request from browser the location authorization. +Once granted, an event listener is set on any position update, so it can update the +map state and the markers position. This method can be called again, only if the +geolocation watch has been cleared ; for example when updating the accuracy options. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A Promise resolved when preferences are set

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _initMap() → {Promise}

        + + + + + + +
        +
        +The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite), +add scale control, remove zoom control and set map bounds. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A Promise resolved when preferences are set

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _initMarkers() → {Promise}

        + + + + + + +
        +
        +The _initEvents() method will initialize all saved marker into the map. +Markers must be retrieved from server with a specific format to ensure it works +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A Promise resolved when preferences are set

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _loadAside(type) → {Promise}

        + + + + + + +
        +
        +The _loadAside() method is a generic method to load an HTML template and replace +the aside DOM content with that template, aswell as updating the document's class. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        type + + +String + + + +

        The aside to load in login/register/forgot-password

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A Promise resolved when template is loaded and in DOM

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (private, static) _loadForgotPasswordAside()

        + + + + + + +
        +
        +The _loadForgotPasswordAside() method will load the forgot password content into the aside +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _loadLoginAside()

        + + + + + + +
        +
        +The _loadLoginAside() method will load the login content into the aside +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _loadRegisterAside()

        + + + + + + +
        +
        +The _loadRegisterAside() method will load the register content into the aside +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (async, private, static) _markPopupFactory(options) → {Promise}

        + + + + + + +
        +
        +The _markPopupFactory() method will create the associated popup DOM for a given mark. It will +fetch the popup template, replace its content with its i18n and provide its tooltip. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        options + + +Object + + + +

        The marker options

        +
        Properties
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        type + + +String + + + +

        The marker type in spot/shop/bar/user

        lat + + +Float + + + +

        The marker latitude

        lng + + +Float + + + +

        The marker longitude

        user + + +String + + + +

        The user that discovered the marker

        description + + +String + + + +

        The marker description

        rate + + +Float + + + +

        The marker rate

        + +
        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A promise resolved with the popup DOM element

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (private, static) _setMarkerLabels(marks, visible)

        + + + + + + +
        +
        +The _setMarkerLabels() method will set the label visibility for an array of marks +depending on the `visible` argument value. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        marks + + +Array.<Object> + + + +

        The array of marks to edit visibility from

        visible + + +Boolean + + + +

        The labels visibility state to apply

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _toggleAside()

        + + + + + + +
        +
        +The _toggleAside() method will expand or collapse the aside, depending on the +`this._isAsideExpanded` flag state. To be used as a callba, adding useful parameters to url before redirectck on aside expander. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/BeerCrackerzAuth.js.html b/front/doc/BeerCrackerzAuth.js.html new file mode 100644 index 0000000..857b596 --- /dev/null +++ b/front/doc/BeerCrackerzAuth.js.html @@ -0,0 +1,1041 @@ + + + + + JSDoc: Source: BeerCrackerzAuth.js + + + + + + + + + + +
        + +

        Source: BeerCrackerzAuth.js

        + + + + + + +
        +
        +
        import './BeerCrackerzAuth.scss';
        +import Kom from './js/core/Kom.js';
        +import LangManager from './js/core/LangManager.js';
        +
        +import VisuHelper from './js/ui/VisuHelper.js';
        +import ZoomSlider from './js/ui/component/ZoomSlider.js';
        +
        +import Utils from './js/utils/Utils.js';
        +import AccuracyEnum from './js/utils/enums/AccuracyEnum.js';
        +import ClustersEnum from './js/utils/enums/ClusterEnum.js';
        +import ProvidersEnum from './js/utils/enums/ProviderEnum.js';
        +import MapEnum from './js/utils/enums/MapEnum.js';
        +import MarkersEnum from './js/utils/enums/MarkerEnum.js';
        +
        +
        +class BeerCrackerzAuth {
        +
        +
        +  /**
        +   * @summary The BeerCrackerzAuth main component
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * This component handles all the authentication pages for BeerCrackerz. It provides the login, the
        +   * register and the forgot password process. It also provides a public map so unauthenticated user
        +   * can still browse the best BeerCrackerz spots. For more information, please consult the application
        +   * description page at <a href="https://about.beercrackerz.org">https://about.beercrackerz.org/</a>
        +   * </blockquote>
        +   **/
        +  constructor() {
        +    /**
        +     * The minimal user object holds position and accuracy
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._user = {
        +      lat: 48.853121540141096, // Default lat to Paris Notre-Dame latitude
        +      lng: 2.3498955769881156, // Default lng to Paris Notre-Dame longitude
        +      accuracy: 0 // Accuracy in meter given by geolocation API
        +    };
        +    /**
        +     * The stored marks for spots, shops and bars
        +     * @type {Object}
        +     * @private
        +     **/
        +     this._marks = {
        +      spot: [],
        +      shop: [],
        +      bar: []
        +    };
        +    /**
        +     * The stored clusters for markers, see Leaflet.markercluster plugin
        +     * @type {Object}
        +     * @private
        +     **/
        +     this._clusters = {
        +      spot: {},
        +      shop: {},
        +      bar: {}
        +    };
        +    /**
        +     * The Aside DOM container
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._aside = null;
        +    /**
        +     * The Aside expand status
        +     * @type {Boolean}
        +     * @private
        +     **/
        +    this._isAsideExpanded = true;
        +    /**
        +     * The server communication class
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._kom = null;
        +    /**
        +     * The frontend i18n manager
        +     * @type {Object}
        +     * @private
        +     **/
        +    this._lang = new LangManager();
        +
        +    this._init();
        +  }
        +
        +
        +  // ======================================================================== //
        +  // -------------------------- App initialization -------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name _init
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _init() method handle the whole app initialization sequence. It first
        +   * set the aside content to login (as it comes with the base welcome.html template),
        +   * then initialize the communication and notification handler, and will finally
        +   * initialize the whole map, markers and interactivity.
        +   * </blockquote>
        +   **/
        +  _init() {
        +    this.nls.updateLang(Utils.getPreference('selected-lang')).then(() => {
        +    // By default, the template contains the login aside, no need to fetch it
        +    this._handleLoginAside();
        +    this._kom = new Kom();
        +    // We ensure the Kom layer is valid and ready to go any further
        +    if (this._kom.isValid === true) {
        +      const urlSearchParams = new URLSearchParams(window.location.search);
        +      const params = Object.fromEntries(urlSearchParams.entries());
        +      if (params.activate) {
        +        const error = document.getElementById('login-error');
        +        error.classList.add('visible');
        +        if (params.activate === 'True') {
        +          error.classList.add('success');
        +          error.innerHTML = this.nls.register('activationSuccess');
        +        } else {
        +          error.innerHTML = this.nls.register('activationError');
        +        }
        +      } else if (params.uidb64 && params.token) {
        +        this._loadForgotPasswordAside(params);
        +      }
        +
        +      this._initMap()
        +        .then(this._initGeolocation.bind(this))
        +        .then(this._initMarkers.bind(this))
        +        .then(this._initEvents.bind(this))
        +        .catch(this._fatalError.bind(this));
        +    } else {
        +      this._fatalError({
        +        file: 'Kom.js',
        +        msg: (this._kom.csrf === null) ? `The CSRF token doesn't exists in cookies` : `The headers amount is invalid`
        +      });
        +    }
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @async
        +   * @name _initMap
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite),
        +   * add scale control, remove zoom control and set map bounds.
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initMap() {
        +    return new Promise(resolve => {
        +      // Use main div to inject OSM into
        +      this._map = window.L.map('beer-crakerz-map', {
        +        zoomControl: false,
        +      }).setView([48.853121540141096, 2.3498955769881156], 12);
        +      // Add meter and feet scale on map
        +      window.L.control.scale().addTo(this._map);
        +      // Place user marker on the map
        +      this._drawUserMarker();
        +      // Prevent panning outside of the world's edge
        +      this._map.setMaxBounds(MapEnum.mapBounds);
        +      // Add layer group to interface
        +      const baseMaps = {};
        +      baseMaps[`<p>${this.nls.map('planLayerOSM')}</p>`] = ProvidersEnum.planOsm;
        +      baseMaps[`<p>${this.nls.map('satLayerEsri')}</p>`] = ProvidersEnum.satEsri;
        +      // Append layer depending on user preference
        +      ProvidersEnum.planOsm.addTo(this._map);
        +      // Add layer switch radio on bottom right of the map
        +      window.L.control.layers(baseMaps, {}, { position: 'bottomright' }).addTo(this._map);
        +      // Init zoom slider when map has been created
        +      this._zoomSlider = new ZoomSlider(this._map);
        +      resolve();
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @async
        +   * @name _initGeolocation
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _initGeolocation() method will request from browser the location authorization.
        +   * Once granted, an event listener is set on any position update, so it can update the
        +   * map state and the markers position. This method can be called again, only if the
        +   * geolocation watch has been cleared ; for example when updating the accuracy options.
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initGeolocation() {
        +    return new Promise(resolve => {
        +      if ('geolocation' in navigator) {
        +        this._watchId = navigator.geolocation.watchPosition(position => {
        +          // Update saved user position
        +          this._user.lat = position.coords.latitude;
        +          this._user.lng = position.coords.longitude;
        +          this._user.accuracy = position.coords.accuracy;
        +          // Only draw marker if map is already created
        +          if (this._map) {
        +            this._drawUserMarker();
        +          }
        +        }, null, AccuracyEnum.high);
        +        resolve();
        +      } else {
        +        resolve();
        +      }
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @async
        +   * @name _initMarkers
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _initEvents() method will initialize all saved marker into the map.
        +   * Markers must be retrieved from server with a specific format to ensure it works
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +  _initMarkers() {
        +    return new Promise(resolve => {
        +      // Init map clusters for marks to be displayed (disable clustering at opened popup zoom level)
        +      this._clusters.spot = ClustersEnum.spot;
        +      this._clusters.shop = ClustersEnum.shop;
        +      this._clusters.bar = ClustersEnum.bar;
        +
        +      this._map.addLayer(this._clusters.spot);
        +      this._map.addLayer(this._clusters.shop);
        +      this._map.addLayer(this._clusters.bar);
        +
        +      const iterateMarkers = mark => {
        +        this._markPopupFactory(mark).then(dom => {
        +          mark.dom = dom;
        +          mark.marker = this._createMarker(mark);
        +          this._marks[mark.type].push(mark);
        +          this._clusters[mark.type].addLayer(mark.marker);
        +        });
        +      };
        +
        +      this._kom.getSpots().then(spots => {
        +        for (let i = 0; i < spots.length; ++i) {
        +          iterateMarkers(spots[i]);
        +        }
        +      });
        +
        +      this._kom.getShops().then(shops => {
        +        for (let i = 0; i < shops.length; ++i) {
        +          iterateMarkers(shops[i]);
        +        }
        +      });
        +
        +      this._kom.getBars().then(bars => {
        +        for (let i = 0; i < bars.length; ++i) {
        +          iterateMarkers(bars[i]);
        +        }
        +      });
        +
        +      resolve();
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @async
        +   * @name _initEvents
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _initEvents() method will listen to all required events to manipulate the map. Those events
        +   * are both for commands and for map events (click, drag, zoom and layer change).
        +   * </blockquote>
        +   * @returns {Promise} A Promise resolved when preferences are set
        +   **/
        +   _initEvents() {
        +    return new Promise(resolve => {
        +      // Map is dragged by user mouse/finger
        +      this._map.on('drag', () => {
        +        // Constrain pan to the map bounds
        +        this._map.panInsideBounds(MapEnum.mapBounds, { animate: true });
        +      });
        +      // Auto hide labels if zoom level is too high (and restore it when needed)
        +      this._map.on('zoomend', () => {
        +        if (this._map.getZoom() < 15) {
        +          this._setMarkerLabels(this._marks.spot, false);
        +          this._setMarkerLabels(this._marks.shop, false);
        +          this._setMarkerLabels(this._marks.bar, false);
        +        } else {
        +          this._setMarkerLabels(this._marks.spot, true);
        +          this._setMarkerLabels(this._marks.shop, true);
        +          this._setMarkerLabels(this._marks.bar, true);
        +        }
        +      });
        +      // Clustering events
        +      this._clusters.spot.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'spot'));
        +      this._clusters.shop.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'shop'));
        +      this._clusters.bar.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'bar'));
        +      // Center on command
        +      document.getElementById('center-on').addEventListener('click', () => {
        +        this._map.flyTo([this._user.lat, this._user.lng], 18);
        +      });
        +      resolve();
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _fatalError
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _fatalError() method will handle all fatal errors from which the app
        +   * can't recover. It redirects to the error page and send info through the referrer
        +   * so the error page can properly displays it to the user
        +   * </blockquote>
        +   * @param {Object} err - The error object with its info
        +   * @param {Number} [err.status] - The HTTP error code
        +   * @param {String} [err.url] - The URL that generated the HTTP error
        +   * @param {String} [err.file] - The file in which the fatal error happened
        +   * @param {String} [err.msg] - The custom error message
        +   **/
        +  _fatalError(err) {
        +    if (window.DEBUG === false) { // In production, do the actual redirection
        +      // We add params to referrer then redirect to error page so the information can be displayed
        +      if (err && err.status) { // HTTP or related error
        +        window.history.pushState('', '', `/welcome?&page=welcome&code=${err.status}&url=${err.url}&msg=${err.msg}`);
        +      } else if (err && err.file && err.msg) { // File or process error
        +        window.history.pushState('', '', `/welcome?&page=welcome&file=${err.file}&msg=${err.msg}`);
        +      } else { // Generic error fallback
        +        window.history.pushState('', '', `/welcome?&page=welcome&file=BeerCrackerzAuth.js&msg=An unknown error occured`);
        +      }
        +      // Now redirect the user to error page
        +      window.location.href = '/error';
        +    } else {
        +      console.error(err);
        +    }
        +  }
        +
        +
        +  // ======================================================================== //
        +  // -------------------------- Aside interactivity ------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name _toggleAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _toggleAside() method will expand or collapse the aside, depending on the
        +   * `this._isAsideExpanded` flag state. To be used as a callba, adding useful parameters to url before redirectck on aside expander.
        +   * </blockquote>
        +   **/
        +  _toggleAside() {
        +    if (this._isAsideExpanded === true) { // Collapsing aside
        +      this._isAsideExpanded = false;
        +      document.documentElement.style.setProperty('--aside-offset', '-40rem');
        +      document.getElementById('aside-expander-icon').src = '/static/img/logo/left.svg';
        +      document.getElementById('page-header').classList.add('visible');
        +      setTimeout(() => document.getElementById('aside-expander').style.left = '-5rem', 300);
        +    } else { // Expanding aside
        +      this._isAsideExpanded = true;
        +      document.documentElement.style.setProperty('--aside-offset', '0rem');
        +      document.getElementById('aside-expander-icon').src = '/static/img/logo/right.svg';
        +      document.getElementById('aside-expander').style.left = '0';
        +      document.getElementById('page-header').classList.remove('visible');
        +    }
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @async
        +   * @name _loadAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _loadAside() method is a generic method to load an HTML template and replace
        +   * the aside DOM content with that template, aswell as updating the document's class.
        +   * </blockquote>
        +   * @param {String} type - The aside to load in login/register/forgot-password
        +   * @returns {Promise} A Promise resolved when template is loaded and in DOM
        +   **/
        +  _loadAside(type) {
        +    return new Promise((resolve, reject) => {
        +      this._kom.getTemplate(`/aside/${type}`).then(dom => {
        +        //document.body.className = 'login dark-theme'; // Clear previous css class
        +        document.body.classList.add(type); // Update body class with current aside view
        +        // We need to get aside at the last moment because of nls that changed HTML content
        +        this._aside = document.getElementById('aside');
        +        this._aside.innerHTML = ''; // Clear HTML content
        +        this._aside.appendChild(dom); // Replace with current aside dom
        +        resolve();
        +      }).catch(reject);
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _loadLoginAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _loadLoginAside() method will load the login content into the aside
        +   * </blockquote>
        +   **/
        +  _loadLoginAside(checkMail = false) {
        +    this._loadAside('login').then(this._handleLoginAside.bind(this, checkMail)).catch(err => {
        +      err.msg = `Couldn't fetch or build the login aside`;
        +      this._fatalError(err);
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _loadRegisterAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _loadRegisterAside() method will load the register content into the aside
        +   * </blockquote>
        +   **/
        +  _loadRegisterAside() {
        +    this._loadAside('register').then(this._handleRegisterAside.bind(this)).catch(err => {
        +      err.msg = `Couldn't fetch or build the register aside`;
        +      this._fatalError(err);
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _loadForgotPasswordAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _loadForgotPasswordAside() method will load the forgot password content into the aside
        +   * </blockquote>
        +   **/
        +  _loadForgotPasswordAside(params) {
        +    if (params.uidb64 && params.token) {
        +      this._loadAside('resetpassword').then(this._handleResetPasswordAside.bind(this, params)).catch(err => {
        +        err.msg = `Couldn't fetch or build the forgot password aside`;
        +        this._fatalError(err);
        +      });
        +    } else {
        +      this._loadAside('forgotpassword').then(this._handleForgotPasswordAside.bind(this)).catch(err => {
        +        err.msg = `Couldn't fetch or build the forgot password aside`;
        +        this._fatalError(err);
        +      });
        +    }
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _handleLoginAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _handleLoginAside() method will replace the aside content with the login template,
        +   * then it will handle its i18n, and all of its interactivity to submit login form to the server.
        +   * </blockquote>
        +   **/
        +  _handleLoginAside(checkMail = false) {
        +    // Update page nls according to browser language
        +    document.title = this.nls.login('headTitle');
        +    this.nls.handleLoginAside(document.getElementById('aside'));
        +
        +    const error = document.getElementById('login-error');
        +    const username = document.getElementById('username');
        +    const password = document.getElementById('password');
        +
        +    if (checkMail === true) {
        +      error.classList.add('visible');
        +      error.innerHTML = this.nls.login('checkMail');
        +    }
        +
        +    // useful login method for field check and server response check
        +    const _frontFieldValidation = () => {
        +      error.className = 'error';
        +      // Handling empty error cases
        +      if (username.value === '' && password.value === '') {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.login('bothEmpty');
        +        username.classList.add('error');
        +        password.classList.add('error');
        +        return false;
        +      } else if (username.value === '') {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.login('usernameEmpty');
        +        username.classList.add('error');
        +        return false;
        +      } else if (password.value === '') {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.login('passwordEmpty');
        +        password.classList.add('error');
        +        return false;
        +      }
        +      return true;
        +    };
        +    const _backValidation = () => {
        +      // Check response and handle status codes
        +      // If all front and back tests are ok, redirect to auth
        +      // If the user manually force redirection to authindex,
        +      // the server should reject the request as the user is not authenticated
        +      window.location = '/';
        +    };
        +    const _submit = () => {
        +      // Reset error css classes
        +      error.classList.remove('visible');
        +      username.classList.remove('error');
        +      password.classList.remove('error');
        +      if (_frontFieldValidation()) {
        +        this._kom.post('/api/auth/login/', {
        +          username: username.value,
        +          password: password.value
        +        }).then(_backValidation).catch(() => {
        +          error.classList.add('visible');
        +          error.innerHTML = this.nls.login('serverError');
        +        });
        +      }
        +    };
        +    // Submit click event
        +    document.getElementById('login-submit').addEventListener('click', _submit.bind(this), false);
        +    password.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } });
        +    // Register event
        +    document.getElementById('register-aside').addEventListener('click', this._loadRegisterAside.bind(this), false);
        +    document.getElementById('forgot-password').addEventListener('click', this._loadForgotPasswordAside.bind(this), false);
        +    document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _handleRegisterAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _handleRegisterAside() method will replace the aside content with the register template,
        +   * then it will handle its i18n, and all of its interactivity to submit register form to the server.
        +   * </blockquote>
        +   **/
        +  _handleRegisterAside() {
        +    // Update page nls according to browser language
        +    const aside = document.getElementById('aside');
        +    document.title = this.nls.register('headTitle');
        +    this.nls.handleRegisterAside(aside);
        +    const error = document.getElementById('register-error');
        +    const username = document.getElementById('username');
        +    const mail = document.getElementById('mail');
        +    const password1 = document.getElementById('password1');
        +    const password2 = document.getElementById('password2');
        +    // useful login method for field check and server response check
        +    const _frontFieldValidation = () => {
        +      // Handling empty error cases
        +      if (username.value === '' || mail.value === '' || password1.value === '' || password2.value === '') {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.register('fieldEmpty');
        +        if (username.value === '') { username.classList.add('error'); }
        +        if (mail.value === '') { mail.classList.add('error'); }
        +        if (password1.value === '') { password1.classList.add('error'); }
        +        if (password2.value === '') { password2.classList.add('error'); }
        +        return false;
        +      } else if (password1.value !== password2.value) {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.register('notMatchingPassword');
        +        password1.classList.add('error');
        +        password2.classList.add('error');
        +        return false;
        +      }
        +      return true;
        +    };
        +    const _backValidation = () => {
        +      // Redirect aside to login
        +      this._loadLoginAside(true);
        +    };
        +    const _submit = () => {
        +      // Reset error css classes
        +      error.classList.remove('visible');
        +      username.classList.remove('error');
        +      mail.classList.remove('error');
        +      password1.classList.remove('error');
        +      password2.classList.remove('error');
        +      if (_frontFieldValidation()) {
        +        this._kom.post('/api/auth/register/', {
        +          username: username.value,
        +          email: mail.value,
        +          password1: password1.value,
        +          password2: password2.value
        +        }).then(_backValidation).catch(() => {
        +          error.classList.add('visible');
        +          error.innerHTML = this.nls.register('serverError');
        +        });
        +      }
        +    };
        +    // Submit click event
        +    document.getElementById('register-submit').addEventListener('click', _submit.bind(this), false);
        +    password2.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } });
        +    // Register event
        +    document.getElementById('login-aside').addEventListener('click', this._loadLoginAside.bind(this), false);
        +    document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _handleForgotPasswordAside
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _handleForgotPasswordAside() method will replace the aside content with the fogot password
        +   * template, then it will handle its i18n, and all of its interactivity to submit forgot password
        +   * form to the server.
        +   * </blockquote>
        +   **/
        +  _handleForgotPasswordAside() {
        +    // Update page nls according to browser language
        +    const aside = document.getElementById('aside');
        +    document.title = this.nls.forgotPassword('headTitle');
        +    this.nls.handleForgotPasswordAside(aside);
        +    const error = document.getElementById('forgot-password-error');
        +    const mail = document.getElementById('mail');
        +    // useful login method for field check and server response check
        +    const _frontFieldValidation = () => {
        +      // Handling empty error cases
        +      if (mail.value === '') {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.forgotPassword('fieldEmpty');
        +        if (mail.value === '') {
        +          mail.classList.add('error');
        +        }
        +        return false;
        +      }
        +      return true;
        +    };
        +    const _backValidation = () => {
        +      // Check response and handle status codes
        +      error.classList.add('visible');
        +      error.innerHTML = this.nls.login('checkMail');
        +    };
        +    const _submit = () => {
        +      // Reset error css classes
        +      error.classList.remove('visible');
        +      mail.classList.remove('error');
        +      if (_frontFieldValidation()) {
        +        this._kom.post('/api/auth/password-reset-request/', {
        +          email: mail.value
        +        }, null).then(_backValidation).catch(() => {
        +          error.classList.add('visible');
        +          error.innerHTML = this.nls.forgotPassword('serverError');
        +        });
        +      }
        +    };
        +    // Submit click event
        +    document.getElementById('forgot-password-submit').addEventListener('click', _submit.bind(this), false);
        +    mail.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } });
        +
        +    document.getElementById('login-aside').addEventListener('click', this._loadLoginAside.bind(this), false);
        +    document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false);
        +  }
        +
        +
        +  _handleResetPasswordAside(params) {
        +    // Update page nls according to browser language
        +    const aside = document.getElementById('aside');
        +    document.title = this.nls.resetPassword('headTitle');
        +    this.nls.handleResetPasswordAside(aside);
        +    const error = document.getElementById('reset-password-error');
        +    const password1 = document.getElementById('password1');
        +    const password2 = document.getElementById('password2');
        +    // useful login method for field check and server response check
        +    const _frontFieldValidation = () => {
        +      // Handling empty error cases
        +      if (password1.value === '' || password2.value === '') {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.resetPassword('fieldEmpty');
        +        if (password1.value === '') { password1.classList.add('error'); }
        +        if (password2.value === '') { password2.classList.add('error'); }
        +        return false;
        +      } else if (password1.value !== password2.value) {
        +        error.classList.add('visible');
        +        error.innerHTML = this.nls.resetPassword('notMatchingPassword');
        +        password1.classList.add('error');
        +        password2.classList.add('error');
        +        return false;
        +      }
        +      return true;
        +    };
        +    const _backValidation = () => {
        +      // Redirect aside to login
        +      this._loadLoginAside();
        +    };
        +    const _submit = () => {
        +      // Reset error css classes
        +      error.classList.remove('visible');
        +      password1.classList.remove('error');
        +      password2.classList.remove('error');
        +      if (_frontFieldValidation()) {
        +        this._kom.post(`/api/auth/password-reset/?uidb64=${params.uidb64}&token=${params.token}`, {
        +          password1: password1.value,
        +          password2: password2.value
        +        }, null).then(_backValidation).catch(() => {
        +          error.classList.add('visible');
        +          error.innerHTML = this.nls.resetPassword('serverError');
        +        });
        +      }
        +    };
        +    // Submit click event
        +    document.getElementById('reset-password-submit').addEventListener('click', _submit.bind(this), false);
        +    password2.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } });
        +
        +    document.getElementById('login-aside').addEventListener('click', this._loadLoginAside.bind(this), false);
        +    document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false);
        +  }
        +
        +
        +  // ======================================================================== //
        +  // -------------------------- Public map methods -------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name _drawUserMarker
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _drawUserMarker() method will draw the user marker to the position received
        +   * from the geolocation API. If the marker doesn't exist yet, it will create it and
        +   * place it to its default position (see constructor/this._user).
        +   * </blockquote>
        +   **/
        +  _drawUserMarker() {
        +    if (!this.user.marker) { // Create user marker if not existing
        +      this.user.type = 'user';
        +      this.user.marker = this._createMarker(this.user);
        +    } else { // Update user marker position, range, and accuracy circle
        +      this.user.marker.setLatLng(this.user);
        +    }
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _createMarker
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _createMarker() method will create all BeerCrackerz kind of markers (spot/shop/bar/user),
        +   * will create if needed its popup (if provided in options) and will make it interactive to click.
        +   * </blockquote>
        +   * @param {Object} options - The marker options
        +   * @param {String} options.type - The marker type in spot/shop/bar/user
        +   * @param {Float} options.lat - The marker latitude
        +   * @param {Float} options.lng - The marker longitude
        +   * @param {HTMLElement} [options.dom] - The marker popup content
        +   * @returns {HTMLElement} The Leaflet marker extended with option properties
        +  **/
        +   _createMarker(options) {
        +    let icon = MarkersEnum.black;
        +    if (options.type === 'spot') {
        +      icon = MarkersEnum.green;
        +    } else if (options.type === 'shop') {
        +      icon = MarkersEnum.blue;
        +    } else if (options.type === 'bar') {
        +      icon = MarkersEnum.red;
        +    } else if (options.type === 'user') {
        +      icon = MarkersEnum.user;
        +    }
        +
        +    const marker = window.L.marker([options.lat, options.lng], { icon: icon }).on('click', () => {
        +      // Actual fly to the marker
        +      this.map.flyTo([options.lat, options.lng], 18);
        +    });
        +
        +    if (options.dom) {
        +      marker.bindPopup(options.dom);
        +    }
        +    // All markers that are not spot/shop/bar should be appended to the map
        +    if (['spot', 'shop', 'bar'].indexOf(options.type) === -1) {
        +      marker.addTo(this.map);
        +    }
        +
        +    return marker;
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @async
        +   * @name _markPopupFactory
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _markPopupFactory() method will create the associated popup DOM for a given mark. It will
        +   * fetch the popup template, replace its content with its i18n and provide its tooltip.
        +   * </blockquote>
        +   * @param {Object} options - The marker options
        +   * @param {String} options.type - The marker type in spot/shop/bar/user
        +   * @param {Float} options.lat - The marker latitude
        +   * @param {Float} options.lng - The marker longitude
        +   * @param {String} options.user - The user that discovered the marker
        +   * @param {String} options.description - The marker description
        +   * @param {Float} options.rate - The marker rate
        +   * @returns {Promise} A promise resolved with the popup DOM element
        +  **/
        +  _markPopupFactory(options) {
        +    return new Promise(resolve => {
        +      this._kom.getTemplate(`/popup/${options.type}`).then(dom => {
        +        const element = document.createElement('DIV');
        +        element.appendChild(dom);
        +        const user = options.user;
        +        const desc = Utils.stripDom(options.description) || this.nls.popup(`${options.type}NoDesc`);
        +        const date = new Intl.DateTimeFormat(this.nls.fullLang, { dateStyle: 'long' }).format(new Date(options.creationDate));
        +        this.nls.markPopup(element, {
        +          type: options.type,
        +          name: options.name,
        +          user: user,
        +          rate: options.rate,
        +          desc: desc,
        +          date: date
        +        });
        +
        +        // Fill mark rate (rating is in [0, 4] explaining the +1 in loop bound)
        +        const rate = element.querySelector(`#${options.type}-rating`);
        +        for (let i = 0; i < options.rate + 1; ++i) {
        +          rate.children[i].classList.add('active');
        +        }
        +        // Remove edition buttons if marker is not user's one, this does not replace a server test for edition...
        +        element.querySelector('#popup-social').parentNode.removeChild(element.querySelector('#popup-social'));
        +        element.querySelector('#popup-edit').parentNode.removeChild(element.querySelector('#popup-edit'));
        +        // Create label for new marker
        +        options.tooltip = window.L.tooltip({
        +          permanent: true,
        +          direction: 'center',
        +          className: 'marker-tooltip',
        +          interactive: true
        +        }).setContent(options.name)
        +          .setLatLng(options); // Lat/Lng are embeded in options
        +        // Make tooltip visible if preference is to true
        +        options.tooltip.addTo(this.map);
        +        // Send back the popup
        +        resolve(element);
        +      });
        +    });
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name _setMarkerLabels
        +   * @private
        +   * @memberof BeerCrackerzAuth
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * The _setMarkerLabels() method will set the label visibility for an array of marks
        +   * depending on the `visible` argument value.
        +   * </blockquote>
        +   * @param {Object[]} marks - The array of marks to edit visibility from
        +   * @param {Boolean} visible - The labels visibility state to apply
        +  **/
        +  _setMarkerLabels(marks, visible) {
        +    for (let i = 0; i < marks.length; ++i) {
        +      if (visible) {
        +        marks[i].tooltip.addTo(this.map);
        +      } else {
        +        marks[i].tooltip.removeFrom(this.map);
        +      }
        +    }
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ---------------------------- Class accessors --------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} map
        +   * Leaflet.js map getter
        +   **/
        +   get map() {
        +    return this._map;
        +  }
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} marks
        +   * Leaflet.js marks that holds spot/shop/bar marks as subkeys
        +   **/
        +  get marks() {
        +    return this._marks;
        +  }
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} user
        +   * The session user object
        +   **/
        +  get user() {
        +    return this._user;
        +  }
        +
        +
        +  /**
        +   * @public
        +   * @property {Object} nls
        +   * The LangManager getter
        +   **/
        +  get nls() {
        +    return this._lang;
        +  }
        +
        +
        +}
        +
        +
        +export default BeerCrackerzAuth;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/CustomEvents.html b/front/doc/CustomEvents.html new file mode 100644 index 0000000..3209445 --- /dev/null +++ b/front/doc/CustomEvents.html @@ -0,0 +1,2419 @@ + + + + + JSDoc: Class: CustomEvents + + + + + + + + + + +
        + +

        Class: CustomEvents

        + + + + + + +
        + +
        + +

        CustomEvents(debugopt)

        + + +
        + +
        +
        + + + + + + +

        new CustomEvents(debugopt)

        + + + +

        JavaScript regular and custom events abstraction

        + + + + +
        +
        The CustomEvents class provides an abstraction of JavaScript event listener, to allow +easy binding and removing those events. It also provides an interface to register custom events. This class is +meant to be used on all scopes you need ; module or global. Refer to each public method for detailed features. +For source code, please go to +https://github.com/ArthurBeaulieu/CustomEvents.js
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeAttributesDefaultDescription
        debug + + +boolean + + + + + + <optional>
        + + + + + +
        + + false + +

        Debug flag ; when true, logs will be output in JavaScript console at each event

        + + + + + + +
        + + + + +
        Since:
        +
        • June 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + +

        Members

        + + + +

        (private) _customEvents :object

        + + + + +
        +

        We store custom events by name as key, each key stores an Array of subscribed events

        +
        + + + +
        Type:
        +
          +
        • + +object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _debug :boolean

        + + + + +
        +

        Internal logging flag from constructor options, allow to output each event action

        +
        + + + +
        Type:
        +
          +
        • + +boolean + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _idIncrementor :number

        + + + + +
        +

        Start the ID incrementer at pseudo random value, used for both regular and custom events

        +
        + + + +
        Type:
        +
          +
        • + +number + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _regularEvents :Array.<any>

        + + + + +
        +

        We store classical event listeners in array of objects containing all their information

        +
        + + + +
        Type:
        +
          +
        • + +Array.<any> + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        version :string

        + + + + +
        +

        Component version

        +
        + + + +
        Type:
        +
          +
        • + +string + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + +

        Methods

        + + + + + + + +

        (private, static) _clearRegularEvent(index) → {boolean}

        + + + + + + +
        +
        _clearRegularEvent method remove the saved event listener for a +given index in regularEvents array range.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        index + + +number + + + +

        The regular event index to remove from class attributes

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The method status ; true for success, false for not cleared any event
        • +
        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (private, static) _raise(level, errorValue)

        + + + + + + +
        +
        Internal method to abstract console wrapped in debug flag.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        level + + +string + + + +

        The console method to call

        errorValue + + +string + + + +

        The error value to display in console method

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (static) addEvent(eventName, element, callback, scopeopt, optionsopt) → {number|boolean}

        + + + + + + +
        +
        addEvent method abstracts the addEventListener method to easily +remove it when needed, also to set a custom scope on callback.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeAttributesDefaultDescription
        eventName + + +string + + + + + + + + + + + +

        The event name to fire (mousemove, click, context etc.)

        element + + +object + + + + + + + + + + + +

        The DOM element to attach the listener to

        callback + + +function + + + + + + + + + + + +

        The callback function to execute when event is realised

        scope + + +object + + + + + + <optional>
        + + + + + +
        + + element + +

        The event scope to apply to the callback (optional, default to DOM element)

        options + + +object +| + +boolean + + + + + + <optional>
        + + + + + +
        + + false + +

        The event options (useCapture and else)

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The event ID to use to manually remove an event, false if arguments are invalid
        • +
        +
        + + + +
        +
        + Type +
        +
        + +number +| + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) destroy()

        + + + + + + +
        +
        CustomEvents destructor. Will remove all event listeners and keys in instance.
        +
        + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (static) publish(eventName, dataopt) → {boolean}

        + + + + + + +
        +
        Publish method allow you to fire an event by name and trigger all its subscription by callbacks./blockquote> +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeAttributesDescription
        eventName + + +string + + + + + + + + + +

        Event name (the one to use to publish)

        data + + +object + + + + + + <optional>
        + + + + + +

        The data object to sent through the custom event

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The method status ; true for success, false for non-existing event
        • +
        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) removeAllEvents() → {boolean}

        + + + + + + +
        +
        Clear all event listener registered through this class object.
        +
        + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The method status ; true for success, false for not removed any event
        • +
        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) removeEvent(eventId) → {boolean}

        + + + + + + +
        +
        removeEvent method abstracts the removeEventListener method to +really remove event listeners.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        eventId + + +number + + + +

        The event ID to remove listener from. Returned when addEvent is called

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The method status ; true for success, false for non-existing event
        • +
        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) subscribe(eventName, callback, oneShotopt) → {number|boolean}

        + + + + + + +
        +
        Subscribe method allow you to listen to an event and react when it occurs.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeAttributesDefaultDescription
        eventName + + +string + + + + + + + + + + + +

        Event name (the one to use to publish)

        callback + + +function + + + + + + + + + + + +

        The callback to execute when event is published

        oneShot + + +boolean + + + + + + <optional>
        + + + + + +
        + + false + +

        One shot : to remove subscription the first time callback is fired

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The event id, to be used when manually unsubscribing
        • +
        +
        + + + +
        +
        + Type +
        +
        + +number +| + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) unsubscribe(eventId) → {boolean}

        + + + + + + +
        +
        Unsubscribe method allow you to revoke an event subscription from its string name.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        eventId + + +number + + + +

        The subscription id returned when subscribing to an event name

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The method status ; true for success, false for non-existing subscription
        • +
        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) unsubscribeAllFor(eventName) → {boolean}

        + + + + + + +
        +
        unsubscribeAllFor method clear all subscriptions registered for given event name.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        eventName + + +string + + + +

        The event to clear subscription from

        + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The method status ; true for success, false for non-existing event
        • +
        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:24 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/DropElement.html b/front/doc/DropElement.html new file mode 100644 index 0000000..cccebc2 --- /dev/null +++ b/front/doc/DropElement.html @@ -0,0 +1,1856 @@ + + + + + JSDoc: Class: DropElement + + + + + + + + + + +
        + +

        Class: DropElement

        + + + + + + +
        + +
        + +

        DropElement(options)

        + + +
        + +
        +
        + + + + + + +

        new DropElement(options)

        + + + +

        Make any DOM element drop friendly

        + + + + +
        +
        This class will make any DOM element able to receive drop event. It propose an overlay +when the target is hovered with a draggable element. It handle both the desktop and the mobile behavior. It must be +used with a DragElement class for perfect compatibility!
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        options + + +object + + + +

        The element to drop options

        +
        Properties
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        target + + +object + + + +

        The element to allow dropping in

        + +
        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + +

        Members

        + + + +

        (private) _evtIds :Array.<number>

        + + + + +
        +

        The event IDs for all mobile and desktop dropping events

        +
        + + + +
        Type:
        +
          +
        • + +Array.<number> + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _movementCounter :number

        + + + + +
        +

        This counter helps to avoid enter/leave events to overlap when target has children

        +
        + + + +
        Type:
        +
          +
        • + +number + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _onDropCB :function

        + + + + +
        +

        The callback function to call on each drop event

        +
        + + + +
        Type:
        +
          +
        • + +function + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _target :object

        + + + + +
        +

        The element to make allow dropping in

        +
        + + + +
        Type:
        +
          +
        • + +object + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _transparentBorder :string

        + + + + +
        +

        The transparent border that must be added to avoid weird target resize on hover

        +
        + + + +
        Type:
        +
          +
        • + +string + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + +

        Methods

        + + + + + + + +

        (private, static) _buildElements()

        + + + + + + +
        +
        This method will define the transparent border style and append this virtual border to the +target DOM element.
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _dragEnter(event)

        + + + + + + +
        +
        This method will handle the entering of a dragged div over the target DOM element. When +the target DOM element is hovered, a dashed border is made visible, replacing the transparent one to notify the +user that the dragged div can be dropped.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        event + + +object + + + +

        The mouse event

        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _dragLeave(event)

        + + + + + + +
        +
        This method will handle the event that is fired when the hovered div leaves the target +DOM element. It require the movement counter to be equal to zero to restore the transparent border of the target +DOM element.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        event + + +object + + + +

        The mouse event

        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _dragOver(event)

        + + + + + + +
        +
        This method will handle the dragged div hovering the target DOM element.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        event + + +object + + + +

        The mouse event

        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _drop(event)

        + + + + + + +
        +
        This method will handle the dropping of a DragElement, to properly read the data it holds +and send it to the drop callback provided in constructor.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        event + + +object + + + +

        The mouse or touch event

        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _eventBehavior(event)

        + + + + + + +
        +
        This method will prevent the default behavior of given event, and will stop its +propagation.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        event + + +object + + + +

        The mouse or touch event

        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _events()

        + + + + + + +
        +
        This method will subscribe to drop events, both for desktop and mobile.
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _isTouchEventInTarget(touchPosition) → {boolean}

        + + + + + + +
        +
        This method will compare a touch point to the target position and return true if the +touch point is inside the target DOM element.
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        touchPosition + + +object + + + +

        The touch event

        + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        Do the touch point is included in the target DOM element

        +
        + + + +
        +
        + Type +
        +
        + +boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) destroy()

        + + + + + + +
        +
        This method will unsubscribe all drop events and remove all properties.
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • December 2020
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:24 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/Kom.html b/front/doc/Kom.html new file mode 100644 index 0000000..4aa73e7 --- /dev/null +++ b/front/doc/Kom.html @@ -0,0 +1,2384 @@ + + + + + JSDoc: Class: Kom + + + + + + + + + + +
        + +

        Class: Kom

        + + + + + + +
        + +
        + +

        Kom()

        + + +
        + +
        +
        + + + + + + +

        new Kom()

        + + + +

        Server communication abstraction

        + + + + +
        +
        +This class is the main object to deal with when requesting something from the server. +It handle all urls calls (GET, POST), treat responses or handle errors using +Promise.
        Because it uses Promise, success and errors are to be handled in the caller +function, using .then() and .catch(). To properly deal with POST request, +the session must contain a csrf token in cookies. Otherwise, those POST call may fail. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + +

        Members

        + + + +

        (private) _csrfToken :string

        + + + + +
        +

        User session CSRF token to use in POST request

        +
        + + + +
        Type:
        +
          +
        • + +string + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        (private) _headers :Array.<Array>

        + + + + +
        +

        Array of HTTP headers to be used in HTTP calls

        +
        + + + +
        Type:
        +
          +
        • + +Array.<Array> + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + +

        isValid :Boolean

        + + + + +
        +

        Wether the Kom class headers are properly built

        +
        + + + +
        Type:
        +
          +
        • + +Boolean + + +
        • +
        + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + +

        Methods

        + + + + + + + +

        (private, static) _checkValidity()

        + + + + + + +
        +
        +Check the Kom instance validity (eough headers) to ensure its properties validity. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (private, static) _createRequestHeaders() → {Array.<Array>}

        + + + + + + +
        +
        +Fills Kom _headers private member array, to use in HTTP requests later on. +This method is required to be called on construction. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The headers array, length 3, to be used in HTTP requests
        • +
        +
        + + + +
        +
        + Type +
        +
        + +Array.<Array> + + +
        +
        + + + + + + + + + + + + + +

        (private, static) _getCsrfCookie() → {String}

        + + + + + + +
        +
        +Extract CSRF token value from client cookies and returns it as a string. Returns an empty +string by default. This method is required to be called on construction. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The CSRF token string
        • +
        +
        + + + +
        +
        + Type +
        +
        + +String + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _resolveAs(type, response) → {Promise}

        + + + + + + +
        +
        +Generic tool method used by private methods on fetch responses to format output in the provided +format. It must be either `json`, `text`, `raw` or `dom`. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        type + + +String + + + +

        The type of resolution, can be json, text, raw or dom

        response + + +Object + + + +

        The fetch response object

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise, format response as an object on resolve, as error code string on reject

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _resolveAsDom(response) → {Promise}

        + + + + + + +
        +
        +Tool method used by public methods on fetch responses to format output data as DOM fragment to be +read in JavaScript code as HTML template. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        response + + +Object + + + +

        The fetch response object

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise, format response as a string on resolve, as error code string on reject

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _resolveAsJSON(response) → {Promise}

        + + + + + + +
        +
        +Tool method used by public methods on fetch responses to format output data as JSON to be +read in JavaScript code as objects. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        response + + +Object + + + +

        The fetch response object

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise, format response as an object on resolve, as error code string on reject

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, private, static) _resolveAsText(response) → {Promise}

        + + + + + + +
        +
        +Tool method used by public methods on fetch responses to format output data as text to be +read in JavaScript code as string (mostly to parse HTML templates). +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        response + + +Object + + + +

        The fetch response object

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise, format response as a string on resolve, as error code string on reject

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, static) get(url) → {Promise}

        + + + + + + +
        +
        +GET HTTP request using the fetch API.
        resolve returns the +response as an Object.
        reject returns an error key as a String. +It is meant to perform API call to access database through the user interface. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        url + + +String + + + +

        The GET url to fetch data from, in supported back URLs

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, static) getText(url) → {Promise}

        + + + + + + +
        +
        +GET HTTP request using the fetch API.
        resolve returns the +response as a String.
        reject returns an error key as a String. It is +meant to perform API call to get HTML templates as string to be parsed as documents/documents fragments. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        url + + +String + + + +

        The GET url to fetch data from, in supported back URLs

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, static) getText(url) → {Promise}

        + + + + + + +
        +
        +GET HTTP request using the fetch API.
        resolve returns the +response as a String.
        reject returns an error key as a String. It is +meant to perform API call to get HTML templates as string to be parsed as documents/documents fragments. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        url + + +String + + + +

        The GET url to fetch data from, in supported back URLs

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, static) post(url, data) → {Promise}

        + + + + + + +
        +
        +POST HTTP request using the fetch API.
        Beware that the given options +object match the url expectations.
        resolve +returns the response as an Object.
        reject returns an error key as a String. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        url + + +String + + + +

        The POST url to fetch data from

        data + + +Object + + + +

        The JSON object that contains POST parameters

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +

        (async, static) postText(url, data) → {Promise}

        + + + + + + +
        +
        +POST HTTP request using the fetch API.
        Beware that the given options +object match the url expectations.
        resolve +returns the response as a String.
        reject returns an error key as a String. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        url + + +String + + + +

        The POST url to fetch data from

        data + + +Object + + + +

        The JSON object that contains POST parameters

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The request Promise

        +
        + + + +
        +
        + Type +
        +
        + +Promise + + +
        +
        + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:24 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/LangManager.html b/front/doc/LangManager.html new file mode 100644 index 0000000..7de5bb9 --- /dev/null +++ b/front/doc/LangManager.html @@ -0,0 +1,279 @@ + + + + + JSDoc: Class: LangManager + + + + + + + + + + +
        + +

        Class: LangManager

        + + + + + + +
        + +
        + +

        LangManager(lang, cb, err)

        + + +
        + +
        +
        + + + + + + +

        new LangManager(lang, cb, err)

        + + + +

        Handle i18n for BeerCrackerz

        + + + + +
        +
        +This class will fetch and store all i18n keys for a given language to be used in BeerCrackerz. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        lang + + +String + + + +

        The selected language to fetch

        cb + + +function + + + +

        The callback to call once i18n keys are loaded

        err + + +function + + + +

        The callback to call if anything went wrong

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:24 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/Utils.html b/front/doc/Utils.html new file mode 100644 index 0000000..5e6e364 --- /dev/null +++ b/front/doc/Utils.html @@ -0,0 +1,1052 @@ + + + + + JSDoc: Class: Utils + + + + + + + + + + +
        + +

        Class: Utils

        + + + + + + +
        + +
        + +

        Utils()

        + + +
        + +
        +
        + + + + + + +

        new Utils()

        + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + +

        Methods

        + + + + + + + +

        (static) getDistanceBetweenCoords(from, to) → {Number}

        + + + + + + +
        +
        +Compute the distance in meters between two points given in [Lat, Lng] arrays. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        from + + +Array + + + +

        The first point lat and lng array

        to + + +Array + + + +

        The second point lat and lng array

        + + + + + + +
        + + + + +
        Since:
        +
        • November 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        A floating number, the distance between two points given in meters

        +
        + + + +
        +
        + Type +
        +
        + +Number + + +
        +
        + + + + + + + + + + + + + +

        (static) precisionRound(value, precision) → {Number}

        + + + + + + +
        +
        +Do a Math.round with a given precision (ie amount of integers after the coma). +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        value + + +Nunmber + + + +

        The value to precisely round (> 0)

        precision + + +Number + + + +

        The number of integers after the coma (> 0)

        + + + + + + +
        + + + + +
        Since:
        +
        • September 2018
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +
          +
        • The rounded value
        • +
        +
        + + + +
        +
        + Type +
        +
        + +Number + + +
        +
        + + + + + + + + + + + + + +

        (static) replaceString(element, string, value) → {Boolean}

        + + + + + + +
        +
        +Replace a given string in an HTML element with another. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        element + + +Element + + + +

        The DOM element to replace string in

        string + + +String + + + +

        The string to be replaced

        value + + +String + + + +

        The value to apply to the replaced string

        + + + + + + +
        + + + + +
        Since:
        +
        • November 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The success status of the replace action

        +
        + + + +
        +
        + Type +
        +
        + +Boolean + + +
        +
        + + + + + + + + + + + + + +

        (static) setDefaultPreferences()

        + + + + + + +
        +
        +Analyze preferences and fallback to default values if preferences doesn't exists. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • November 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +

        (static) stripDom(html) → {String}

        + + + + + + +
        +
        +From a given string/number input, this method will strip all unnecessary +characters and will only retrun the text content as a string. +
        +
        + + + + + + + + + +
        Parameters:
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        NameTypeDescription
        html + + +String +| + +Number + + + +

        The html string to strip

        + + + + + + +
        + + + + +
        Since:
        +
        • November 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The stripped text content, empty string on error

        +
        + + + +
        +
        + Type +
        +
        + +String + + +
        +
        + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:24 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/VisuHelper.html b/front/doc/VisuHelper.html new file mode 100644 index 0000000..c4263fb --- /dev/null +++ b/front/doc/VisuHelper.html @@ -0,0 +1,292 @@ + + + + + JSDoc: Class: VisuHelper + + + + + + + + + + +
        + +

        Class: VisuHelper

        + + + + + + +
        + +
        + +

        VisuHelper()

        + + +
        + +
        +
        + + + + + + +

        new VisuHelper()

        + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + + + + + + + +
        + + + + + + + + + + + + + + + + +

        Methods

        + + + + + + + +

        (static) initDebugUI() → {Element}

        + + + + + + +
        +
        +Will build the debug UI element and chain them as expected. +
        +
        + + + + + + + + + + + + + +
        + + + + +
        Since:
        +
        • November 2022
        + + + + + + + + + + + + + + + +
        Author:
        +
        +
          +
        • Arthur Beaulieu
        • +
        +
        + + + + + + + + + +
        Source:
        +
        + + + + + + + +
        + + + + + + + + + + + + + + + +
        Returns:
        + + +
        +

        The debug DOM element for BeerCrackerz

        +
        + + + +
        +
        + Type +
        +
        + +Element + + +
        +
        + + + + + + + + + + + + + +
        + +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:24 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Bold-webfont.eot b/front/doc/fonts/OpenSans-Bold-webfont.eot similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Bold-webfont.eot rename to front/doc/fonts/OpenSans-Bold-webfont.eot diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Bold-webfont.svg b/front/doc/fonts/OpenSans-Bold-webfont.svg similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Bold-webfont.svg rename to front/doc/fonts/OpenSans-Bold-webfont.svg diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Bold-webfont.woff b/front/doc/fonts/OpenSans-Bold-webfont.woff similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Bold-webfont.woff rename to front/doc/fonts/OpenSans-Bold-webfont.woff diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-BoldItalic-webfont.eot b/front/doc/fonts/OpenSans-BoldItalic-webfont.eot similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-BoldItalic-webfont.eot rename to front/doc/fonts/OpenSans-BoldItalic-webfont.eot diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-BoldItalic-webfont.svg b/front/doc/fonts/OpenSans-BoldItalic-webfont.svg similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-BoldItalic-webfont.svg rename to front/doc/fonts/OpenSans-BoldItalic-webfont.svg diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-BoldItalic-webfont.woff b/front/doc/fonts/OpenSans-BoldItalic-webfont.woff similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-BoldItalic-webfont.woff rename to front/doc/fonts/OpenSans-BoldItalic-webfont.woff diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Italic-webfont.eot b/front/doc/fonts/OpenSans-Italic-webfont.eot similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Italic-webfont.eot rename to front/doc/fonts/OpenSans-Italic-webfont.eot diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Italic-webfont.svg b/front/doc/fonts/OpenSans-Italic-webfont.svg similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Italic-webfont.svg rename to front/doc/fonts/OpenSans-Italic-webfont.svg diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Italic-webfont.woff b/front/doc/fonts/OpenSans-Italic-webfont.woff similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Italic-webfont.woff rename to front/doc/fonts/OpenSans-Italic-webfont.woff diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Light-webfont.eot b/front/doc/fonts/OpenSans-Light-webfont.eot similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Light-webfont.eot rename to front/doc/fonts/OpenSans-Light-webfont.eot diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Light-webfont.svg b/front/doc/fonts/OpenSans-Light-webfont.svg similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Light-webfont.svg rename to front/doc/fonts/OpenSans-Light-webfont.svg diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Light-webfont.woff b/front/doc/fonts/OpenSans-Light-webfont.woff similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Light-webfont.woff rename to front/doc/fonts/OpenSans-Light-webfont.woff diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-LightItalic-webfont.eot b/front/doc/fonts/OpenSans-LightItalic-webfont.eot similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-LightItalic-webfont.eot rename to front/doc/fonts/OpenSans-LightItalic-webfont.eot diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-LightItalic-webfont.svg b/front/doc/fonts/OpenSans-LightItalic-webfont.svg similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-LightItalic-webfont.svg rename to front/doc/fonts/OpenSans-LightItalic-webfont.svg diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-LightItalic-webfont.woff b/front/doc/fonts/OpenSans-LightItalic-webfont.woff similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-LightItalic-webfont.woff rename to front/doc/fonts/OpenSans-LightItalic-webfont.woff diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Regular-webfont.eot b/front/doc/fonts/OpenSans-Regular-webfont.eot similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Regular-webfont.eot rename to front/doc/fonts/OpenSans-Regular-webfont.eot diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Regular-webfont.svg b/front/doc/fonts/OpenSans-Regular-webfont.svg similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Regular-webfont.svg rename to front/doc/fonts/OpenSans-Regular-webfont.svg diff --git a/doc/beercrackerz/0.0.1/fonts/OpenSans-Regular-webfont.woff b/front/doc/fonts/OpenSans-Regular-webfont.woff similarity index 100% rename from doc/beercrackerz/0.0.1/fonts/OpenSans-Regular-webfont.woff rename to front/doc/fonts/OpenSans-Regular-webfont.woff diff --git a/front/doc/index.html b/front/doc/index.html new file mode 100644 index 0000000..750f5e2 --- /dev/null +++ b/front/doc/index.html @@ -0,0 +1,86 @@ + + + + + JSDoc: Home + + + + + + + + + + +
        + +

        Home

        + + + + + + + + +

        + + + + + + + + + + + + + + + +
        +

        BeerCrackerz

        +

        Welcome, fellow beer lovers. BeerCrackerz is a community web app to list the best spots to drink a fresh one while you're outside. It provides a well-known map interface so it is really easy to browse, find or add unique spots!

        +

        You want to try it ? We are currently running an instance just so you can try (and add your personnal best places) :

        +

        https://beercrackerz.org

        +

        Get Started

        +

        To run your instance, your need to install docker, docker-compose and npm on your system :

        +
          +
        • clone the repository on your system ;
        • +
        • set the conf.env values for ports, database users and pass and API keys
        • +
        • run docker-compose build
        • +
        • run docker-compose up -d
        • +
        • create super user with docker exec -it beer_crackerz_back python manage.py createsuperuser
        • +
        • configure your system web server (or reverse proxy) according to conf.env parameters
        • +
        +

        About

        +

        BeerCrackerz is an open-source software edited and hosted by Messe Basse Production, developper by Arthur Beaulieu and Raphaël Beekmann

        +

        Technologies

        +

        OpenStreetMap, ESRI, LeafletJs, Leaflet-MarkerCluster, Leaflet-Color-Markers, SVGRepo

        +
        + + + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + \ No newline at end of file diff --git a/front/doc/js_core_Kom.js.html b/front/doc/js_core_Kom.js.html new file mode 100644 index 0000000..da08003 --- /dev/null +++ b/front/doc/js_core_Kom.js.html @@ -0,0 +1,620 @@ + + + + + JSDoc: Source: js/core/Kom.js + + + + + + + + + + +
        + +

        Source: js/core/Kom.js

        + + + + + + +
        +
        +
        class Kom {
        +
        +
        +  /** 
        +   * @summary Server communication abstraction
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * This class is the main object to deal with when requesting something from the server.
        +   * It handle all urls calls (<code>GET</code>, <code>POST</code>), treat responses or handle errors using
        +   * <code>Promise</code>.<br>Because it uses <code>Promise</code>, success and errors are to be handled in the caller
        +   * function, using <code>.then()</code> and <code>.catch()</code>. To properly deal with <code>POST</code> request,
        +   * the session must contain a csrf token in cookies. Otherwise, those <code>POST</code> call may fail.
        +   * </blockquote> 
        +   **/
        +  constructor() {
        +    /** 
        +     * User session CSRF token to use in POST request
        +     * @type {string}
        +     * @private
        +     **/
        +    this._csrfToken = this._getCsrfCookie();
        +    /** 
        +     * Array of HTTP headers to be used in HTTP calls
        +     * @type {Array[]}
        +     * @private
        +     **/
        +    this._headers = this._createRequestHeaders();
        +    /** 
        +     * Wether the Kom class headers are properly built
        +     * @type {Boolean}
        +     * @public
        +     **/
        +    this.isValid = this._checkValidity(); // Check that CSRF token exists and that headers are properly created
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ------------------------- Class initialization ------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /**
        +   * @method
        +   * @name _getCsrfCookie
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Extract CSRF token value from client cookies and returns it as a string. Returns an empty
        +   * string by default. This method is required to be called on construction.
        +   * </blockquote>
        +   * @return {String} - The CSRF token string 
        +   **/
        +  _getCsrfCookie() {
        +    if (document.cookie && document.cookie !== '') {
        +      const cookies = document.cookie.split(';');
        +      for (let i = 0; i < cookies.length; ++i) {
        +        // Parse current cookie to extract its properties
        +        const cookie = cookies[i].split('=');
        +        if (cookie !== undefined && cookie[0].toLowerCase().includes('srf')) {
        +          // Found a matching cookie for csrftoken value, return as decoded string
        +          return decodeURIComponent(cookie[1]);
        +        }
        +      }
        +    }
        +    // Return empty string by default, POST calls may fail
        +    return null;
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @name _createRequestHeaders
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Fills Kom <code>_headers</code> private member array, to use in HTTP requests later on.
        +   * This method is required to be called on construction.
        +   * </blockquote>
        +   * @return {Array[]} - The headers array, length 3, to be used in HTTP requests 
        +   **/
        +  _createRequestHeaders() {
        +    return [
        +      ['Content-Type', 'application/json; charset=UTF-8'],
        +      ['Accept', 'application/json'],
        +      ['X-CSRFToken', this._csrfToken]
        +    ];
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @name _checkValidity
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Check the Kom instance validity (eough headers) to ensure its properties validity.
        +   * </blockquote> 
        +   **/
        +  _checkValidity() {
        +    if (this._csrfToken !== null) {
        +      if (this._headers.length !== 3) {
        +        return false;
        +      }
        +    } else {
        +      return false;
        +    }
        +
        +    return true;
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ------------------------- Response formatting -------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name _resolveAs
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Generic tool method used by private methods on fetch responses to format output in the provided
        +   * format. It must be either `json`, `text`, `raw` or `dom`.
        +   * </blockquote>
        +   * @param {String} type - The type of resolution, can be `json`, `text`, `raw` or `dom`
        +   * @param {Object} response - The <code>fetch</code> response object
        +   * @returns {Promise} The request <code>Promise</code>, format response as an object on resolve, as error code string on reject 
        +   **/
        +  _resolveAs(type, response) {
        +    return new Promise((resolve, reject) => {
        +      if (response) {
        +        if (type === 'raw') { // Raw are made in XMLHttpRequest and need special handling
        +          if (response.status === 200) {
        +            resolve(response.responseText);
        +          } else {
        +            reject(response.status);
        +          }
        +        } else if (type === 'json' || type === 'text') { // Call are made using fetch API
        +          if (response[type]) {
        +            resolve(response[type]());
        +          } else { // Fallback on standard error handling
        +            reject(response.status);
        +          }
        +        } else if (type === 'dom') {
        +          response.text().then(html => {
        +            resolve(document.createRange().createContextualFragment(html));
        +          }).catch(reject);
        +        } else { // Resolution type doesn't exists, resolving empty
        +          resolve();
        +        }
        +      } else {
        +        reject('F_KOM_MISSING_ARGUMENT');
        +      }
        +    });
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name _resolveAsJSON
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Tool method used by public methods on fetch responses to format output data as JSON to be
        +   * read in JavaScript code as objects.
        +   * </blockquote>
        +   * @param {Object} response - The <code>fetch</code> response object
        +   * @returns {Promise} The request <code>Promise</code>, format response as an object on resolve, as error code string on reject 
        +   **/
        +  _resolveAsJSON(response) {
        +    return this._resolveAs('json', response);
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name _resolveAsText
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Tool method used by public methods on fetch responses to format output data as text to be
        +   * read in JavaScript code as string (mostly to parse HTML templates).
        +   * </blockquote>
        +   * @param {Object} response - The <code>fetch</code> response object
        +   * @returns {Promise} The request <code>Promise</code>, format response as a string on resolve, as error code string on reject
        +   **/
        +  _resolveAsText(response) {
        +    return this._resolveAs('text', response);
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name _resolveAsDom
        +   * @private
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * Tool method used by public methods on fetch responses to format output data as DOM fragment to be
        +   * read in JavaScript code as HTML template.
        +   * </blockquote>
        +   * @param {Object} response - The <code>fetch</code> response object
        +   * @returns {Promise} The request <code>Promise</code>, format response as a string on resolve, as error code string on reject
        +   **/
        +  _resolveAsDom(response) {
        +    return this._resolveAs('dom', response);
        +  }
        +
        +
        +
        +  // ======================================================================== //
        +  // --------------------------- GET server calls --------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name get
        +   * @public
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * <code>GET</code> HTTP request using the fetch API.<br><code>resolve</code> returns the
        +   * response as an <code>Object</code>.<br><code>reject</code> returns an error key as a <code>String</code>.
        +   * It is meant to perform API call to access database through the user interface.
        +   * </blockquote>
        +   * @param {String} url - The <code>GET</code> url to fetch data from, in supported back URLs
        +   * @returns {Promise} The request <code>Promise</code> 
        +   **/
        +  get(url, resolution = this._resolveAsJSON.bind(this)) {
        +    return new Promise((resolve, reject) => {
        +      const options = {
        +        method: 'GET',
        +        headers: new Headers([this._headers[0]]) // Content type to JSON
        +      };
        +
        +      fetch(url, options)
        +        .then(data => {
        +          // In case the request wen well but didn't gave the expected 200 status
        +          if (data.status !== 200) {
        +            reject(data);
        +          } 
        +          return resolution(data);         
        +        })
        +        .then(resolve)
        +        .catch(reject);
        +    });
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name getText
        +   * @public
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * <code>GET</code> HTTP request using the fetch API.<br><code>resolve</code> returns the
        +   * response as a <code>String</code>.<br><code>reject</code> returns an error key as a <code>String</code>. It is
        +   * meant to perform API call to get HTML templates as string to be parsed as documents/documents fragments.
        +   * </blockquote>
        +   * @param {String} url - The <code>GET</code> url to fetch data from, in supported back URLs
        +   * @returns {Promise} The request <code>Promise</code>
        +   **/
        +  getText(url) {
        +    return this.get(url, this._resolveAsText.bind(this));
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name getText
        +   * @public
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * <code>GET</code> HTTP request using the fetch API.<br><code>resolve</code> returns the
        +   * response as a <code>String</code>.<br><code>reject</code> returns an error key as a <code>String</code>. It is
        +   * meant to perform API call to get HTML templates as string to be parsed as documents/documents fragments.
        +   * </blockquote>
        +   * @param {String} url - The <code>GET</code> url to fetch data from, in supported back URLs
        +   * @returns {Promise} The request <code>Promise</code> 
        +   **/
        +  getTemplate(url) {
        +    return this.get(url, this._resolveAsDom.bind(this));
        +  }
        +
        +
        +  // ======================================================================== //
        +  // -------------------------- POST server calls --------------------------- //
        +  // ======================================================================== //
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name post
        +   * @public
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * <code>POST</code> HTTP request using the fetch API.<br>Beware that the given options
        +   * object match the url expectations.<br><code>resolve</code>
        +   * returns the response as an <code>Object</code>.<br><code>reject</code> returns an error key as a <code>String</code>.
        +   * </blockquote>
        +   * @param {String} url - The <code>POST</code> url to fetch data from
        +   * @param {Object} data - The <code>JSON</code> object that contains <code>POST</code> parameters
        +   * @returns {Promise} The request <code>Promise</code> 
        +   **/
        +  post(url, data, resolution = this._resolveAsJSON.bind(this)) {
        +    return new Promise((resolve, reject) => {
        +      const options = {
        +        method: 'POST',
        +        headers: new Headers(this._headers), // POST needs all previously defined headers
        +        body: JSON.stringify(data)
        +      };
        +
        +      fetch(url, options)
        +        .then(data => {
        +          // In case the request wen well but didn't gave the expected 200 status
        +          if (data.status >= 400) {
        +            reject(data);
        +          }
        +
        +          if (resolution !== undefined && resolution !== null) {
        +            return resolution(data);
        +          }
        +
        +          return data;
        +        })
        +        .then(resolve)
        +        .catch(reject);
        +    });
        +  }
        +
        +
        +  /** 
        +   * @method
        +   * @async
        +   * @name postText
        +   * @public
        +   * @memberof Kom
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * <code>POST</code> HTTP request using the fetch API.<br>Beware that the given options
        +   * object match the url expectations.<br><code>resolve</code>
        +   * returns the response as a <code>String</code>.<br><code>reject</code> returns an error key as a <code>String</code>.
        +   * </blockquote>
        +   * @param {String} url - The <code>POST</code> url to fetch data from
        +   * @param {Object} data - The <code>JSON</code> object that contains <code>POST</code> parameters
        +   * @returns {Promise} The request <code>Promise</code>
        +   **/
        +  postText(url, data) {
        +    return this.post(url, data, this._resolveAsText.bind(this));
        +  }
        +
        +
        +  postImage(url, data) {
        +    return this.post(url, data, null);
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ------------------------- PATCH server calls --------------------------- //
        +  // ======================================================================== //
        +
        +
        +  patch(url, data, resolution = this._resolveAsJSON.bind(this)) {
        +    return new Promise((resolve, reject) => {
        +      const options = {
        +        method: 'PATCH',
        +        headers: new Headers(this._headers), // PATCH needs all previously defined headers
        +        body: JSON.stringify(data)
        +      };
        +
        +      fetch(url, options)
        +        .then(data => {
        +          // In case the request wen well but didn't gave the expected 200 status
        +          if (data.status >= 400) {
        +            reject(data);
        +          }
        +
        +          if (resolution !== undefined && resolution !== null) {
        +            return resolution(data);
        +          }
        +
        +          return data;
        +        })
        +        .then(resolve)
        +        .catch(reject);
        +    });
        +  }
        +
        +
        +  patchImage(url, data) {
        +    return this.patch(url, data, null);
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ------------------------- DELETE server calls -------------------------- //
        +  // ======================================================================== //
        +
        +
        +  delete(url, data, resolution = this._resolveAsJSON.bind(this)) {
        +    return new Promise((resolve, reject) => {
        +      const options = {
        +        method: 'DELETE',
        +        headers: new Headers(this._headers), // DELETE needs all previously defined headers
        +        body: JSON.stringify(data)
        +      };
        +
        +      fetch(url, options)
        +        .then(data => {
        +          // In case the request wen well but didn't gave the expected 200 status
        +          if (data.status >= 400) {
        +            reject(data);
        +          }
        +
        +          if (resolution !== undefined && resolution !== null) {
        +            return resolution(data);
        +          }
        +
        +          return data;
        +        })
        +        .then(resolve)
        +        .catch(reject);
        +    });
        +  }
        +
        +
        +  // ======================================================================== //
        +  // ------------------ BeerCrackerz server call shortcuts ------------------ //
        +  // ======================================================================== //
        +
        +
        +  _getMarks(type) {
        +    return new Promise((resolve, reject) => {
        +      this.get(`http://localhost:8080/api/${type}`).then(resolve).catch(reject);
        +    });
        +  }
        +
        +
        +  getSpots() {
        +    return this._getMarks('spot');
        +  }
        +
        +
        +  getShops() {
        +    return this._getMarks('shop');
        +  }
        +
        +
        +  getBars() {
        +    return this._getMarks('bar');
        +  }
        +
        +
        +  _saveMark(type, data) {
        +    return this.post(`http://localhost:8080/api/${type}/`, data, this._resolveAsJSON.bind(this));
        +  }
        +
        +
        +  spotCreated(data) {
        +    return this._saveMark('spot', data);    
        +  }
        +
        +
        +  shopCreated(data) {
        +    return this._saveMark('shop', data);    
        +  }
        +
        +
        +  barCreated(data) {
        +    return this._saveMark('bar', data);    
        +  }
        +
        +
        +  _editMark(type, id, data) {
        +    if (!type || !id || !data) { Promise.reject(); }
        +    return this.patch(`http://localhost:8080/api/${type}/${id}/`, data, this._resolveAsJSON.bind(this));
        +  }
        +
        +
        +  spotEdited(id, data) {
        +    return this._editMark('spot', id, data);    
        +  }
        +
        +
        +  shopEdited(id, data) {
        +    return this._editMark('shop', id, data);    
        +  }
        +
        +
        +  barEdited(id, data) {
        +    return this._editMark('bar', id, data);    
        +  }
        +
        +
        +  _deleteMark(type, id, data) {
        +    if (!type || !id || !data) { Promise.reject(); }
        +    return this.delete(`http://localhost:8080/api/${type}/${id}/`, data, null);
        +  }
        +
        +
        +  spotDeleted(id, data) {
        +    return this._deleteMark('spot', id, data);    
        +  }
        +
        +
        +  shopDeleted(id, data) {
        +    return this._deleteMark('shop', id, data);    
        +  }
        +
        +
        +  barDeleted(id, data) {
        +    return this._deleteMark('bar', id, data);    
        +  }
        +
        +
        +  get csrf() {
        +    return null;
        +  }
        +
        +
        +}
        +
        +
        +export default Kom;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_core_LangManager.js.html b/front/doc/js_core_LangManager.js.html new file mode 100644 index 0000000..75288c0 --- /dev/null +++ b/front/doc/js_core_LangManager.js.html @@ -0,0 +1,330 @@ + + + + + JSDoc: Source: js/core/LangManager.js + + + + + + + + + + +
        + +

        Source: js/core/LangManager.js

        + + + + + + +
        +
        +
        import Utils from '../utils/Utils.js';
        +import SupportedLangEnum from '../utils/enums/SupportedLangEnum.js';
        +
        +
        +class LangManager {
        +
        +
        +  /** 
        +   * @summary Handle i18n for BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since September 2022
        +   * @description
        +   * <blockquote>
        +   * This class will fetch and store all i18n keys for a given language to be used in BeerCrackerz.
        +   * </blockquote> 
        +   * @param {String} lang - The selected language to fetch
        +   * @param {Function} cb - The callback to call once i18n keys are loaded
        +   * @param {Function} err - The callback to call if anything went wrong
        +   **/
        +  constructor() {
        +    this._lang = '';
        +    this._fullLang = '';
        +    this._values = {};
        +  }
        +
        +
        +  _init() {
        +    return new Promise((resolve, reject) => {
        +      fetch(`/static/nls/${this._lang}.json`).then(data => {
        +        // In case the request wen well but didn't gave the expected 200 status
        +        if (data.status !== 200) {
        +          data.msg = `Fetching the i18n file failed`;
        +          reject(data);
        +        }
        +
        +        data.text().then(nls => {
        +          try {
        +            this._values = JSON.parse(nls);
        +          } catch (err) {
        +            data.msg = `Parsing the i18n file failed`;
        +            reject(data);
        +          }
        +
        +          resolve();
        +        }).catch(reject);
        +      }).catch(reject);
        +    });
        +  }
        +
        +
        +  updateLang(lang) {
        +    return new Promise((resolve, reject) => {
        +      if (this._lang !== lang) {
        +        this._lang = (SupportedLangEnum.indexOf(lang) !== -1) ? lang : 'en';
        +        this._fullLang = lang;
        +        this._init().then(resolve).catch(reject);
        +      } else {
        +        resolve();
        +      }
        +    });
        +  }
        +
        +
        +  debug(key) {
        +    return this._values.debug[key] || '';
        +  }
        +
        +
        +  notif(key) {
        +    return this._values.notif[key] || '';
        +  }
        +
        +
        +  nav(key) {
        +    return this._values.nav[key] || '';
        +  }
        +
        +
        +  map(key) {
        +    return this._values.map[key] || '';
        +  }
        +
        +
        +  spot(key) {
        +    return this._values.spot[key] || '';
        +  }
        +
        +
        +  shop(key) {
        +    return this._values.shop[key] || '';
        +  }
        +
        +
        +  bar(key) {
        +    return this._values.bar[key] || '';
        +  }
        +
        +
        +  popup(key) {
        +    return this._values.popup[key] || '';
        +  }
        +
        +
        +  modal(key) {
        +    return this._values.modal[key] || '';
        +  }
        +
        +
        +  login(key) {
        +    return this._values.auth.login[key] || '';
        +  }
        +
        +
        +  register(key) {
        +    return this._values.auth.register[key] || '';
        +  }
        +
        +
        +  forgotPassword(key) {
        +    return this._values.auth.forgotPassword[key] || '';    
        +  }
        +
        +
        +  resetPassword(key) {
        +    return this._values.auth.resetPassword[key] || '';    
        +  }
        +
        +
        +  // Auth page shortcut to update UI chunks of text in views 
        +
        +
        +  handleLoginAside(aside) {
        +    Utils.replaceString(aside, '{LOGIN_SUBTITLE}', this.login('subtitle'));
        +    Utils.replaceString(aside, '{LOGIN_HIDDEN_ERROR}', this.login('hiddenError'));
        +    Utils.replaceString(aside, '{LOGIN_USERNAME_LABEL}', this.login('username'));
        +    Utils.replaceString(aside, '{LOGIN_USERNAME_PASSWORD}', this.login('password'));
        +    Utils.replaceString(aside, '{LOGIN_BUTTON}', this.login('login'));
        +    Utils.replaceString(aside, '{LOGIN_NOT_REGISTERED}', this.login('notRegistered'));
        +    Utils.replaceString(aside, '{LOGIN_REGISTER}', this.login('register'));
        +    Utils.replaceString(aside, '{LOGIN_FORGOT_PASSWORD}', this.login('forgot'));
        +    Utils.replaceString(aside, '{LOGIN_PASSWORD_RESET}', this.login('reset'));    
        +  }
        +
        +
        +  handleRegisterAside(aside) {
        +    Utils.replaceString(aside, '{REGISTER_SUBTITLE}', this.register('subtitle'));
        +    Utils.replaceString(aside, '{REGISTER_HIDDEN_ERROR}', this.register('hiddenError'));
        +    Utils.replaceString(aside, '{REGISTER_USERNAME_LABEL}', this.register('username'));
        +    Utils.replaceString(aside, '{REGISTER_MAIL_LABEL}', this.register('mail'));
        +    Utils.replaceString(aside, '{REGISTER_USERNAME_PASSWORD_1}', this.register('password1'));
        +    Utils.replaceString(aside, '{REGISTER_USERNAME_PASSWORD_2}', this.register('password2'));
        +    Utils.replaceString(aside, '{REGISTER_BUTTON}', this.register('register'));
        +    Utils.replaceString(aside, '{REGISTER_ALREADY_DONE}', this.register('notRegistered'));
        +    Utils.replaceString(aside, '{REGISTER_LOGIN}', this.register('login'));    
        +  }
        +
        +
        +  handleForgotPasswordAside(aside) {
        +    Utils.replaceString(aside, '{FORGOT_PASSWORD_SUBTITLE}', this.forgotPassword('subtitle'));
        +    Utils.replaceString(aside, '{FORGOT_PASSWORD_ERROR}', this.register('hiddenError'));
        +    Utils.replaceString(aside, '{FORGOT_PASSWORD_MAIL_LABEL}', this.forgotPassword('mail'));
        +    Utils.replaceString(aside, '{FORGOT_PASSWORD_BUTTON}', this.forgotPassword('submit'));
        +    Utils.replaceString(aside, '{FORGOT_PASSWORD_LOGIN_LABEL}', this.forgotPassword('loginLabel'));
        +    Utils.replaceString(aside, '{FORGOT_PASSWORD_LOGIN}', this.forgotPassword('login'));
        +  }
        +
        +
        +  handleResetPasswordAside(aside) {
        +    Utils.replaceString(aside, '{RESET_PASSWORD_SUBTITLE}', this.resetPassword('subtitle'));
        +    Utils.replaceString(aside, '{RESET_PASSWORD_HIDDEN_ERROR}', this.resetPassword('hiddenError'));
        +    Utils.replaceString(aside, '{RESET_PASSWORD_1}', this.resetPassword('password1'));
        +    Utils.replaceString(aside, '{RESET_PASSWORD_2}', this.resetPassword('password2'));
        +    Utils.replaceString(aside, '{RESET_PASSWORD_BUTTON}', this.resetPassword('reset'));
        +    Utils.replaceString(aside, '{RESET_PASSWORD_LOGIN_LABEL}', this.resetPassword('loginLabel'));
        +    Utils.replaceString(aside, '{RESET_PASSWORD_LOGIN}', this.resetPassword('login'));    
        +  }
        +
        +
        +  // Main App shortcut to update UI chunks of text in views 
        +
        +
        +  addMarkModal(dom, type, action) {
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-title`), `{${type.toUpperCase()}_TITLE}`, this[type](`${action}Title`));
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-subtitle`), `{${type.toUpperCase()}_SUBTITLE}`, this[type]('subtitle'));
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-name`), `{${type.toUpperCase()}_NAME}`, this[type]('nameLabel'));
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-desc`), `{${type.toUpperCase()}_DESC}`, this[type]('descLabel'));
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-rate`), `{${type.toUpperCase()}_RATE}`, this[type]('rateLabel'));
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-type`), `{${type.toUpperCase()}_TYPE}`, this[type]('typeLabel'));
        +    Utils.replaceString(dom.querySelector(`#nls-${type}-modifiers`), `{${type.toUpperCase()}_MODIFIERS}`, this[type]('modifiersLabel'));
        +    Utils.replaceString(dom.querySelector(`#${type}-submit`), `{${type.toUpperCase()}_SUBMIT}`, this.nav(action));
        +    Utils.replaceString(dom.querySelector(`#${type}-close`), `{${type.toUpperCase()}_CANCEL}`, this.nav('cancel'));    
        +    if (dom.querySelector(`#nls-${type}-price`)) {
        +      Utils.replaceString(dom.querySelector(`#nls-${type}-price`), `{${type.toUpperCase()}_PRICE}`, this[type]('priceLabel'));
        +    }
        +  }
        +
        +
        +  deleteMarkModal(dom) {
        +    Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('deleteMarkTitle'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{MODAL_DESC}`, this.modal('deleteMarkDesc'));
        +    Utils.replaceString(dom.querySelector(`#cancel-close`), `{MODAL_CANCEL}`, this.nav('cancel'));
        +    Utils.replaceString(dom.querySelector(`#delete-close`), `{MODAL_DELETE}`, this.nav('delete'));    
        +  }
        +
        +
        +  userProfileModal(dom) {
        +    Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('userTitle'));
        +    Utils.replaceString(dom.querySelector(`#nls-user-high-accuracy`), `{ACCURACY_USER_CHECK}`, this.modal('userAccuracyPref'));
        +    Utils.replaceString(dom.querySelector(`#nls-user-dark-theme`), `{DARK_THEME_CHECK}`, this.modal('darkThemePref'));
        +    Utils.replaceString(dom.querySelector(`#nls-user-debug`), `{DEBUG_USER_CHECK}`, this.modal('userDebugPref'));
        +    Utils.replaceString(dom.querySelector(`#nls-startup-help`), `{STARTUP_HELP}`, this.modal('startupHelp'));
        +    Utils.replaceString(dom.querySelector(`#nls-lang-select`), `{LANG_SELECT}`, this.modal('langPref'));
        +    Utils.replaceString(dom.querySelector(`#nls-lang-fr`), `{LANG_FR}`, this.modal('langFr'));
        +    Utils.replaceString(dom.querySelector(`#nls-lang-en`), `{LANG_EN}`, this.modal('langEn'));
        +    Utils.replaceString(dom.querySelector(`#nls-lang-es`), `{LANG_ES}`, this.modal('langEs'));
        +    Utils.replaceString(dom.querySelector(`#nls-lang-de`), `{LANG_DE}`, this.modal('langDe'));
        +    Utils.replaceString(dom.querySelector(`#nls-lang-pt`), `{LANG_PT}`, this.modal('langPt')); 
        +    Utils.replaceString(dom.querySelector(`#nls-about-desc`), `{BEERCRACKERZ_DESC}`, this.modal('aboutDesc'));
        +    Utils.replaceString(dom.querySelector(`#nls-update-pp`), `{UPDATE_PROFILE_PIC_LABEL}`, this.modal('updatePP'));    
        +  }
        +
        +
        +  updateProfilePictureModal(dom) {
        +    Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('updatePPTitle'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{UPDATE_PP_DESC}`, this.modal('updatePPDesc'));
        +    Utils.replaceString(dom.querySelector(`#update-pp-close`), `{UPDATE_PP_CANCEL}`, this.nav('cancel'));
        +    Utils.replaceString(dom.querySelector(`#update-pp-submit`), `{UPDATE_PP_SUBMIT}`, this.nav('upload'));
        +  }
        +
        +
        +  hideShowModal(dom) {
        +    Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-title`), `{MODAL_TITLE}`, this.modal('hideShowTitle'));
        +    Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-labels`), `{LABELS_HIDESHOW_MODAL}`, this.modal('hideShowLabels'));
        +    Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-circles`), `{CIRCLES_HIDESHOW_MODAL}`, this.modal('hideShowCircles'));
        +    Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-spots`), `{SPOTS_HIDESHOW_MODAL}`, this.modal('hideShowSpots'));
        +    Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-shops`), `{SHOPS_HIDESHOW_MODAL}`, this.modal('hideShowShops'));
        +    Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-bars`), `{BARS_HIDESHOW_MODAL}`, this.modal('hideShowBars'));
        +    Utils.replaceString(dom.querySelector(`#nls-view-helper-label`), `{HELPER_LABEL}`, this.modal('hideShowHelperLabel'));
        +    Utils.replaceString(dom.querySelector(`#modal-close-button`), `{MODAL_CLOSE}`, this.nav('close'));    
        +  }
        +
        +
        +  startupHelpModal(dom) {
        +    Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('helpTitle'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-1-1`), `{MODAL_PAGE_1_1}`, this.modal('helpPage1'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-1-2`), `{MODAL_PAGE_1_2}`, this.modal('helpNavNext'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-2-1`), `{MODAL_PAGE_2_1}`, this.modal('helpPage2'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-2-2`), `{MODAL_PAGE_2_2}`, this.modal('helpNavNextPrev'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-3-1`), `{MODAL_PAGE_3_1}`, this.modal('helpPage3'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-3-2`), `{MODAL_PAGE_3_2}`, this.modal('helpNavNextPrev'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-4-1`), `{MODAL_PAGE_4_1}`, this.modal('helpPage4'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-4-2`), `{MODAL_PAGE_4_2}`, this.modal('helpNavNextPrev'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-5-1`), `{MODAL_PAGE_5_1}`, this.modal('helpPage5'));
        +    Utils.replaceString(dom.querySelector(`#nls-modal-page-5-2`), `{MODAL_PAGE_5_2}`, this.modal('helpNavEnd'));
        +    Utils.replaceString(dom.querySelector(`#modal-quit`), `{MODAL_QUIT}`, this.modal('helpQuit'));
        +    Utils.replaceString(dom.querySelector(`#modal-quit-no-see`), `{MODAL_QUIT_NO_SEE}`, this.modal('helpQuitNoSee'));
        +  }
        +
        +
        +  markPopup(dom, options) {
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_NAME}`, Utils.stripDom(options.name));
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_FINDER}`, options.user);
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_FOUND_BY}`, this.popup(`${options.type}FoundBy`));
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_FOUND_WHEN}`, this.popup(`${options.type}FoundWhen`));
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_FOUND_DATE}`, options.date);
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_RATE}`, `${options.rate + 1}`);
        +    Utils.replaceString(dom, `{${options.type.toUpperCase()}_DESC}`, options.desc);    
        +  }
        +
        +
        +  get fullLang() {
        +    return this._fullLang;
        +  }
        +
        +
        +}
        +
        +
        +export default LangManager;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_ui_VisuHelper.js.html b/front/doc/js_ui_VisuHelper.js.html new file mode 100644 index 0000000..1d858ce --- /dev/null +++ b/front/doc/js_ui_VisuHelper.js.html @@ -0,0 +1,563 @@ + + + + + JSDoc: Source: js/ui/VisuHelper.js + + + + + + + + + + +
        + +

        Source: js/ui/VisuHelper.js

        + + + + + + +
        +
        +
        import Utils from '../utils/Utils.js';
        +import AccuracyEnum from '../utils/enums/AccuracyEnum.js';
        +import ColorEnum from '../utils/enums/ColorEnum.js';
        +import MapEnum from '../utils/enums/MapEnum.js';
        +import MarkersEnum from '../utils/enums/MarkerEnum.js';
        +
        +
        +/**
        + * @class
        + * @static
        + * @public
        +**/
        +class VisuHelper {
        +
        +
        +  /**
        +   * @method
        +   * @name initDebugUI
        +   * @public
        +   * @static
        +   * @memberof VisuHelper
        +   * @author Arthur Beaulieu
        +   * @since November 2022
        +   * @description
        +   * <blockquote>
        +   * Will build the debug UI element and chain them as expected.
        +   * </blockquote>
        +   * @return {Element} The debug DOM element for BeerCrackerz
        +   **/
        +  static initDebugUI() {
        +    const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls);
        +    const debugContainer = document.createElement('DIV');
        +    const title = document.createElement('H1');
        +    const userLat = document.createElement('P');
        +    const userLng = document.createElement('P');
        +    const updatesAmount = document.createElement('P');
        +    const userAccuracy = document.createElement('P');
        +    const highAccuracy = document.createElement('P');
        +    const maxAge = document.createElement('P');
        +    const posTimeout = document.createElement('P');
        +    const zoomLevel = document.createElement('P');
        +    const marks = document.createElement('P');
        +    debugContainer.classList.add('debug-container');
        +    userLat.classList.add('debug-user-lat');
        +    userLng.classList.add('debug-user-lng');
        +    updatesAmount.classList.add('debug-updates-amount');
        +    userAccuracy.classList.add('debug-user-accuracy');
        +    highAccuracy.classList.add('debug-high-accuracy');
        +    maxAge.classList.add('debug-pos-max-age');
        +    posTimeout.classList.add('debug-pos-timeout');
        +    zoomLevel.classList.add('debug-zoom-level');
        +    marks.classList.add('debug-marks-amount');
        +    title.innerHTML = `BeerCrackerz v${window.VERSION}`;
        +    userLat.innerHTML = `<b>${lang('lat')}</b> -`;
        +    userLng.innerHTML = `<b>${lang('lng')}</b> -`;
        +    updatesAmount.innerHTML = `<b>${lang('updates')}</b> 0`;
        +    userAccuracy.innerHTML = `<b>${lang('accuracy')}</b> -`;
        +    highAccuracy.innerHTML = `<b>${lang('highAccuracy')}</b> -`;
        +    maxAge.innerHTML = `<b>${lang('posAge')}</b> -`;
        +    posTimeout.innerHTML = `<b>${lang('posTimeout')}</b> -`;
        +    zoomLevel.innerHTML = `<b>${lang('zoom')}</b> -`;
        +    marks.innerHTML = `<b>${lang('marks')}</b> -`;
        +    debugContainer.appendChild(title);
        +    debugContainer.appendChild(userLat);
        +    debugContainer.appendChild(userLng);
        +    debugContainer.appendChild(updatesAmount);
        +    debugContainer.appendChild(userAccuracy);
        +    debugContainer.appendChild(highAccuracy);
        +    debugContainer.appendChild(maxAge);
        +    debugContainer.appendChild(posTimeout);
        +    debugContainer.appendChild(zoomLevel);
        +    debugContainer.appendChild(marks);
        +    return debugContainer;
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name addDebugUI
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The addDebugUI() method appends the debug DOM element to the document body
        +   * </blockquote>
        +   **/
        +  static addDebugUI() {
        +    document.body.appendChild(window.BeerCrackerz.debugElement);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name removeDebugUI
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The removeDebugUI() method remove the debug DOM element from the document body
        +   * </blockquote>
        +   **/
        +  static removeDebugUI() {
        +    document.body.removeChild(window.BeerCrackerz.debugElement);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name updateDebugUI
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The updateDebugUI() method will update informations held in the debug DOM
        +   * </blockquote>
        +   **/
        +  static updateDebugUI() {
        +    if (window.DEBUG === true) {
        +      const options = (Utils.getPreference('map-high-accuracy') === 'true') ? AccuracyEnum.high : AccuracyEnum.optimized;
        +      const element = window.BeerCrackerz.debugElement;
        +      const user = window.BeerCrackerz.user;
        +      const bc = window.BeerCrackerz;
        +      const lang = bc.nls.debug.bind(bc.nls);
        +      const updateSplittedString = element.querySelector('.debug-updates-amount').innerHTML.split(' ');
        +      const updates = parseInt(updateSplittedString[updateSplittedString.length - 1]) + 1;
        +      const marks = bc.marks.spot.length + bc.marks.shop.length + bc.marks.bar.length;
        +      element.querySelector('.debug-user-lat').innerHTML = `<b>${lang('lat')}</b> ${user.lat}`;
        +      element.querySelector('.debug-user-lng').innerHTML = `<b>${lang('lng')}</b> ${user.lng}`;
        +      element.querySelector('.debug-updates-amount').innerHTML = `<b>${lang('updates')}</b> ${updates}`;
        +      element.querySelector('.debug-user-accuracy').innerHTML = `<b>${lang('accuracy')}</b> ${Utils.precisionRound(user.accuracy, 2)}m`;
        +      element.querySelector('.debug-high-accuracy').innerHTML = `<b>${lang('highAccuracy')}</b> ${options.enableHighAccuracy === true ? lang('enabled') : lang('disabled')}`;
        +      element.querySelector('.debug-pos-max-age').innerHTML = `<b>${lang('posAge')}</b> ${options.maximumAge / 1000}s`;
        +      element.querySelector('.debug-pos-timeout').innerHTML = `<b>${lang('posTimeout')}</b> ${options.timeout / 1000}s`;
        +      element.querySelector('.debug-zoom-level').innerHTML = `<b>${lang('zoom')}</b> ${bc.map.getZoom()}`;
        +      element.querySelector('.debug-marks-amount').innerHTML = `<b>${lang('marks')}</b> ${marks}`;
        +    }
        +  }
        +
        +
        +  static drawUserMarker() {
        +    if (!window.BeerCrackerz.user.marker) { // Create user marker if not existing
        +      window.BeerCrackerz.user.type = 'user';
        +      window.BeerCrackerz.user.marker = VisuHelper.addMark(window.BeerCrackerz.user);
        +      // Append circle around marker for accuracy and range for new marker
        +      window.BeerCrackerz.user.radius = window.BeerCrackerz.user.accuracy;
        +      window.BeerCrackerz.user.circle = VisuHelper.drawCircle(window.BeerCrackerz.user);
        +      window.BeerCrackerz.user.range = VisuHelper.drawCircle({
        +        lat: window.BeerCrackerz.user.lat,
        +        lng: window.BeerCrackerz.user.lng,
        +        radius: MapEnum.newMarkRange,
        +        color: ColorEnum.newMarkRange
        +      });
        +
        +      window.BeerCrackerz.user.circle.addTo(window.BeerCrackerz.map);
        +      window.BeerCrackerz.user.range.addTo(window.BeerCrackerz.map);
        +      // Update circle opacity if pref is at true
        +      if (Utils.getPreference('poi-show-circle') === 'true') {
        +        window.BeerCrackerz.user.circle.setStyle({
        +          opacity: 1,
        +          fillOpacity: 0.1
        +        });
        +        window.BeerCrackerz.user.range.setStyle({
        +          opacity: 1,
        +          fillOpacity: 0.1
        +        });
        +      }
        +      // Callback on marker clicked to add marker on user position
        +      window.BeerCrackerz.user.marker.on('click', window.BeerCrackerz.mapClicked.bind(window.BeerCrackerz));
        +    } else { // Update user marker position, range, and accuracy circle
        +      window.BeerCrackerz.user.marker.setLatLng(window.BeerCrackerz.user);
        +      window.BeerCrackerz.user.range.setLatLng(window.BeerCrackerz.user);
        +      window.BeerCrackerz.user.circle.setLatLng(window.BeerCrackerz.user);
        +      window.BeerCrackerz.user.circle.setRadius(window.BeerCrackerz.user.accuracy);
        +    }    
        +  }
        +
        +
        +  static drawCircle(options) {
        +    return window.L.circle(options, {
        +      color: options.color,
        +      fillColor: options.color,
        +      opacity: 0, // This needs to be updated according to user proximity
        +      fillOpacity: 0, // Same for this parameter
        +      radius: options.radius ? options.radius : MapEnum.socialMarkRange,
        +    });
        +  }
        +
        +
        +  static setMarkerCircles(visible) {
        +    const _updateCircle = list => {
        +      for (let i = 0; i < list.length; ++i) {
        +        // Here we update both opacity and add/remove circle from map
        +        if (visible) {
        +          list[i].circle.setStyle({
        +            opacity: 1,
        +            fillOpacity: 0.1
        +          });
        +          list[i].circle.addTo(window.BeerCrackerz.map);
        +        } else {
        +          list[i].circle.setStyle({
        +            opacity: 0,
        +            fillOpacity: 0
        +          });
        +          list[i].circle.removeFrom(window.BeerCrackerz.map);
        +        }
        +      }
        +    };
        +
        +    const keys = Object.keys(window.BeerCrackerz.marks);
        +    for (let i = 0; i < keys.length; ++i) {
        +      _updateCircle(window.BeerCrackerz.marks[keys[i]]);
        +    }
        +
        +    _updateCircle([ window.BeerCrackerz.user ]);
        +    _updateCircle([{ circle: window.BeerCrackerz.user.range }]);
        +  }
        +
        +
        +  static updateMarkerCirclesVisibility() {   
        +    if (Utils.getPreference('poi-show-circle') === 'true') {
        +      const _updateCircles = list => {
        +        // Check spots in user's proximity
        +        for (let i = 0; i < list.length; ++i) {
        +          // Only update circles that are in user view
        +          if (window.BeerCrackerz.map.getBounds().contains(list[i].marker.getLatLng())) {
        +            const marker = list[i].marker;
        +            const distance = Utils.getDistanceBetweenCoords(
        +              [ window.BeerCrackerz.user.lat, window.BeerCrackerz.user.lng ],
        +              [ marker.getLatLng().lat, marker.getLatLng().lng ]
        +            );
        +            // Only show if user distance to marker is under circle radius
        +            if (distance < MapEnum.socialMarkRange && !list[i].circle.visible) {
        +              list[i].circle.visible = true;
        +              list[i].circle.setStyle({
        +                opacity: 1,
        +                fillOpacity: 0.1
        +              });
        +            } else if (distance >= MapEnum.socialMarkRange && list[i].circle.visible) {
        +              list[i].circle.visible = false;
        +              list[i].circle.setStyle({
        +                opacity: 0,
        +                fillOpacity: 0
        +              });
        +            }
        +          }
        +        }
        +      };
        +
        +      // Update circle visibility according to user distance to them
        +      _updateCircles(window.BeerCrackerz.marks.spot);
        +      _updateCircles(window.BeerCrackerz.marks.shop);
        +      _updateCircles(window.BeerCrackerz.marks.bar);
        +      _updateCircles([ window.BeerCrackerz.user ]);
        +    }
        +  }
        +
        +
        +  static setMarkerLabels(visible) {
        +    const _updateTooltip = list => {
        +      for (let i = 0; i < list.length; ++i) {
        +        if (visible && list[i].clustered === false) {
        +          list[i].tooltip.addTo(window.BeerCrackerz.map);
        +        } else {
        +          list[i].tooltip.removeFrom(window.BeerCrackerz.map);
        +        }
        +      }
        +    };
        +
        +    const keys = Object.keys(window.BeerCrackerz.marks);
        +    for (let i = 0; i < keys.length; ++i) {
        +      _updateTooltip(window.BeerCrackerz.marks[keys[i]]);
        +    }
        +  }
        +
        +
        +  static removeMarkDecoration(mark) {
        +    // Remove label
        +    mark.tooltip.removeFrom(window.BeerCrackerz.map);
        +    // Remove circle
        +    mark.circle.setStyle({
        +      opacity: 0,
        +      fillOpacity: 0
        +    });
        +    mark.circle.removeFrom(window.BeerCrackerz.map);
        +    // Call destroy on mark popup
        +    if (mark.popup) {
        +      mark.popup.destroy();
        +    } 
        +  }
        +
        +
        +  static addMark(mark) {
        +    let icon = MarkersEnum.black;
        +    if (mark.type === 'shop') {
        +      icon = MarkersEnum.blue;
        +    } else if (mark.type === 'spot') {
        +      icon = MarkersEnum.green;
        +    } else if (mark.type === 'bar') {
        +      icon = MarkersEnum.red;
        +    } else if (mark.type === 'user') {
        +      icon = MarkersEnum.user;
        +    }
        +
        +    const marker = window.L.marker([mark.lat, mark.lng], { icon: icon }).on('click', VisuHelper.centerOn.bind(VisuHelper, mark));
        +    if (mark.dom) {
        +      marker.bindPopup(mark.dom);
        +    }
        +    // All markers that are not spot/shop/bar should be appended to the map
        +    if (['spot', 'shop', 'bar'].indexOf(mark.type) === -1) {
        +      marker.addTo(window.BeerCrackerz.map);
        +    }
        +
        +    return marker;
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name toggleLabel
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The toggleLabel() method will, depending on user preference, display or not
        +   * the labels attached to spots/shops/bars marks. This label is basically the
        +   * mark name given by its creator.
        +   * </blockquote>
        +   **/
        +  static toggleLabel() {
        +    const visible = !(Utils.getPreference('poi-show-label') === 'true');
        +    VisuHelper.setMarkerLabels(visible);
        +    Utils.setPreference('poi-show-label', visible);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name toggleCircle
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The toggleCircle() method will, depending on user preference, display or not
        +   * the circles around the spots/shops/bars marks. This circle indicates the minimal
        +   * distance which allow the user to make updates on the mark information
        +   * </blockquote>
        +   **/
        +  static toggleCircle() {
        +    const visible = !(Utils.getPreference('poi-show-circle') === 'true');
        +    VisuHelper.setMarkerCircles(visible);
        +    Utils.setPreference('poi-show-circle', visible);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name toggleMarkers
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The toggleMarkers() method will, depending on user preference, display or not
        +   * a given mark type. This way, the user can fine tune what is displayed on the map.
        +   * A mark type in spots/shops/bars must be given as an argument
        +   * </blockquote>
        +   * @param {String} type - The mark type in spots/tores/bars
        +   **/
        +  static toggleMarkers(event) {
        +    const type = event.target.dataset.type;
        +    const visible = !(Utils.getPreference(`poi-show-${type}`) === 'true');
        +    if (visible === true) {
        +      for (let i = 0; i < window.BeerCrackerz.marks[type].length; ++i) {
        +        window.BeerCrackerz.marks[type][i].visible = true;
        +        window.BeerCrackerz.marks[type][i].circle.setStyle({
        +          opacity: 1,
        +          fillOpacity: 0.1
        +        });
        +      }
        +      window.BeerCrackerz.map.addLayer(window.BeerCrackerz.clusters[type]);
        +    } else {
        +      for (let i = 0; i < window.BeerCrackerz.marks[type].length; ++i) {
        +        window.BeerCrackerz.marks[type][i].visible = false;
        +        window.BeerCrackerz.marks[type][i].circle.setStyle({
        +          opacity: 0,
        +          fillOpacity: 0
        +        });
        +      }
        +      window.BeerCrackerz.map.removeLayer(window.BeerCrackerz.clusters[type]);
        +    }
        +    Utils.setPreference(`poi-show-${type}`, visible);
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name toggleDebug
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The toggleDebug() method will, depending on user preference, add or remove
        +   * the debug DOM element to the user interface. The debug DOM display several
        +   * useful information to identify an issue with the geolocation API
        +   * </blockquote>
        +   **/
        +  static toggleDebug() {
        +    const visible = !window.DEBUG;
        +    window.DEBUG = visible;
        +    Utils.setPreference('app-debug', visible);
        +    if (visible) {
        +      VisuHelper.addDebugUI();
        +    } else {
        +      VisuHelper.removeDebugUI();
        +    }
        +  }
        +
        +
        +  static toggleDarkTheme() {
        +    const isDark = (Utils.getPreference(`dark-theme`) === 'true');
        +    Utils.setPreference('dark-theme', !isDark);
        +    if (isDark === true) {
        +      document.body.classList.remove('dark-theme');
        +      document.body.classList.add('light-theme');
        +    } else {
        +      document.body.classList.remove('light-theme');
        +      document.body.classList.add('dark-theme');
        +    }
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name toggleFocusLock
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since January 2022
        +   * @description
        +   * <blockquote>
        +   * The toggleFocusLock() method will, depending on user preference, lock or unlock
        +   * the map centering around the user marker at each position refresh. This way the user
        +   * can roam while the map is following its position.
        +   * </blockquote>
        +   **/
        +  static toggleFocusLock() {
        +    if (Utils.getPreference('map-center-on-user') === 'true') {
        +      window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif(`unlockFocusOn`));
        +      document.getElementById('center-on').classList.remove('lock-center-on');
        +      Utils.setPreference('map-center-on-user', 'false');
        +    } else {
        +      window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif(`lockFocusOn`));
        +      document.getElementById('center-on').classList.add('lock-center-on');
        +      window.BeerCrackerz.map.flyTo([window.BeerCrackerz.user.lat, window.BeerCrackerz.user.lng], 18);
        +      Utils.setPreference('map-center-on-user', 'true');
        +    }
        +  }
        +
        +
        +  static centerOn(options) {
        +    // Disable center on lock if previously set to true
        +    if (Utils.getPreference('map-center-on-user') === 'true') {
        +      VisuHelper.toggleFocusLock();
        +    }
        +    // Actual fly to the marker
        +    window.BeerCrackerz.map.flyTo([options.lat, options.lng], window.BeerCrackerz.map.getZoom());
        +  }
        +
        +
        +  static toggleStartupHelp() {
        +    if (Utils.getPreference('startup-help') === 'true') {
        +      Utils.setPreference('startup-help', 'false');
        +    } else {
        +      Utils.setPreference('startup-help', 'true');
        +      window.location = '/'; // Force reload so user can see the startup help guide
        +    }
        +  }
        +
        +
        +  static checkClusteredMark(type) {
        +    if (Utils.getPreference('poi-show-label') === 'true') {
        +      const layers = window.BeerCrackerz.marks[type];
        +      for (let i = 0; i < layers.length; ++i) {
        +        const visible = window.BeerCrackerz.map.hasLayer(layers[i].marker);
        +        layers[i].clustered = !visible;
        +        if (visible) {
        +          layers[i].tooltip.addTo(window.BeerCrackerz.map);
        +        } else {
        +          layers[i].tooltip.removeFrom(window.BeerCrackerz.map);
        +        }
        +      }
        +    }
        +  }
        +
        +
        +}
        +
        +
        +export default VisuHelper;
        +  
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_ui_modal_BaseModal.js.html b/front/doc/js_ui_modal_BaseModal.js.html new file mode 100644 index 0000000..c8d25a2 --- /dev/null +++ b/front/doc/js_ui_modal_BaseModal.js.html @@ -0,0 +1,193 @@ + + + + + JSDoc: Source: js/ui/modal/BaseModal.js + + + + + + + + + + +
        + +

        Source: js/ui/modal/BaseModal.js

        + + + + + + +
        +
        +
        class BaseModal {
        +
        +
        +  constructor(type) {
        +    /** @private
        +     * @member {string} - The modal type */
        +    this._type = type;
        +    /** @private
        +     * @member {string} - The HTML template url to fetch */
        +    this._url = `/modal/${this._type}`;
        +    /** @private
        +     * @member {object} - The template root DOM element */
        +    this._rootElement = null;
        +    /** @private
        +     * @member {object} - The overlay that contains the modal, full viewport size and close modal on click */
        +    this._modalOverlay = null;
        +    /** @private
        +     * @member {object} - The close button, in the modal header */
        +    this._closeButton = null;
        +    /** @private
        +     * @member {array} - The event IDs */
        +    this._evtIds = [];
        +
        +    // Modal building sequence:
        +    // - get HTML template from server;
        +    // - parse template response to become DOM object;
        +    // - append DOM element to global overlay;
        +    // - open modal by adding overlay to the body;
        +    // - let child class fill attributes and register its events.
        +    this._loadTemplate();
        +  }
        +
        +
        +  /** @method
        +   * @name destroy
        +   * @public
        +   * @memberof Modal
        +   * @author Arthur Beaulieu
        +   * @since November 2020
        +   * @description <blockquote>This method must be overridden in child class. It only destroys the <code>Modal.js</code>
        +   * properties and close event subscription. The developer must remove its abstracted properties and events after
        +   * calling this method, to make the destruction process complete.</blockquote> **/
        +  destroy() {
        +    for (let i = 0; i < this._evtIds.length; ++i) {
        +      window.Evts.removeEvent(this._evtIds[i]);
        +    }
        +    Object.keys(this).forEach(key => {
        +      delete this[key];
        +    });
        +  }
        +
        +
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +  /*  ------------------------------------  MODAL INSTANTIATION SEQUENCE  ------------------------------------------  */
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +
        +
        +  /** @method
        +   * @name _loadTemplate
        +   * @private
        +   * @async
        +   * @memberof Modal
        +   * @author Arthur Beaulieu
        +   * @since November 2020
        +   * @description <blockquote>This method creates the modal overlay, fetch the HTML template using the <code>Kom.js
        +   * </code> component, it then build the modal DOM, append it to the overlay, open the modal and call <code>
        +   * _fillAttributes()</code> that must be overridden in the child class. It is asynchronous because of the fetch call,
        +   * so the child class constructor can be fully executed.</blockquote> **/
        +  _loadTemplate() {
        +    window.BeerCrackerz.kom.getTemplate(this._url).then(response => {
        +      // Create DOM from fragment and tweak url to only keep modal type as css class
        +      this._rootElement = response.firstElementChild;
        +      this._rootElement.classList.add(`${this._type}-modal`);
        +      // Create overlay modal container
        +      this._modalOverlay = document.createElement('DIV');
        +      this._modalOverlay.id = 'overlay';
        +      this._modalOverlay.classList.add('overlay');
        +      document.body.appendChild(this._modalOverlay);
        +      // Get close button from template
        +      this._closeButton = this._rootElement.querySelector('#modal-close');
        +      this.open();
        +      this._fillAttributes(); // Override in child class to process modal UI
        +    }).catch(error => {
        +      console.error(error);
        +    });
        +  }
        +
        +
        +  /** @method
        +   * @name _fillAttributes
        +   * @private
        +   * @memberof Modal
        +   * @author Arthur Beaulieu
        +   * @since November 2020
        +   * @description <blockquote>This method doesn't implement anything. It must be overridden in child class, to use the
        +   * template DOM elements to build its interactions. It is called once the template is successfully fetched from the
        +   * server.</blockquote> **/
        +  _fillAttributes() {
        +    // Must be overridden in child class to build modal with HTML template attributes
        +  }
        +
        +
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +  /*  ------------------------------------  MODAL VISIBILITY MANIPULATION  -----------------------------------------  */
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +
        +
        +  open() {
        +    this._evtIds.push(window.Evts.addEvent('click', this._modalOverlay, this.close, this));
        +    this._evtIds.push(window.Evts.addEvent('touchend', this._modalOverlay, this.close, this));
        +    this._evtIds.push(window.Evts.addEvent('click', this._closeButton, this.close, this));
        +    this._evtIds.push(window.Evts.addEvent('touchend', this._closeButton, this.close, this));
        +    this._modalOverlay.appendChild(this._rootElement);
        +    this._modalOverlay.style.display = 'flex';
        +    setTimeout(() => this._modalOverlay.style.opacity = 1, 50);
        +  }
        +
        +
        +
        +  close(event, force) {
        +    if (event && event.stopPropagation) {
        +      event.stopPropagation();
        +    }
        +
        +    if (force === true || event.target.id === 'overlay' || event.target.id.indexOf('close') !== -1) {
        +      if (event && event.type === 'touchend' && event.preventDefault) {
        +        event.preventDefault();
        +      }
        +  
        +      this._modalOverlay.style.opacity = 0;
        +      setTimeout(() => {
        +        document.body.removeChild(this._modalOverlay);
        +        this.destroy();
        +      }, 200);
        +    }
        +  }
        +
        +
        +}
        +
        +
        +export default BaseModal;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_ui_modal_MarkModal.js.html b/front/doc/js_ui_modal_MarkModal.js.html new file mode 100644 index 0000000..9fec73f --- /dev/null +++ b/front/doc/js_ui_modal_MarkModal.js.html @@ -0,0 +1,216 @@ + + + + + JSDoc: Source: js/ui/modal/MarkModal.js + + + + + + + + + + +
        + +

        Source: js/ui/modal/MarkModal.js

        + + + + + + +
        +
        +
        import BaseModal from './BaseModal.js';
        +import MarkInfosEnum from '../../utils/enums/MarkInfosEnum.js';
        +import Rating from '../component/Rating.js';
        +
        +
        +// Abstraction for addMark and editMark (as they share same content, not values)
        +class MarkModal extends BaseModal {
        +
        +
        +  constructor(action, options) { // action is add/edit, options.type is mark type
        +    super(`${action}${options.type}`);
        +    this._opts = options; // Save mark options    
        +    this._action = action; // add/edit
        +    this._name = '';
        +    this._description = '';
        +    this._rating = null;
        +    this._pricing = null;
        +  }
        +
        +
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +  /*  ------------------------------------  MODAL INSTANTIATION SEQUENCE  ------------------------------------------  */
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +
        +
        +  /** @method
        +   * @name _fillAttributes
        +   * @private
        +   * @memberof AboutModal
        +   * @author Arthur Beaulieu
        +   * @since November 2020
        +   * @description <blockquote>This method doesn't do anything, the about modal is only for reading.</blockquote> **/
        +  _fillAttributes() {
        +    this._opts.user = window.BeerCrackerz.user.username;
        +    // Generic mark modal section
        +    window.BeerCrackerz.nls.addMarkModal(this._rootElement, this._opts.type, this._action);
        +    // Type and modifier handling
        +    const _elementChecked = event => {
        +      if (event.target.closest('p').classList.contains('selected')) {
        +        event.target.closest('p').classList.remove('selected');
        +      } else {
        +        event.target.closest('p').classList.add('selected');
        +      }
        +    };
        +    // Mark title
        +    this._name = this._rootElement.querySelector(`#${this._opts.type}-name`);
        +    // Handle mark types
        +    const types = this._rootElement.querySelector(`#${this._opts.type}-types`);  
        +    for (let i = 0; i < MarkInfosEnum[this._opts.type].types.length; ++i) {
        +      const type = document.createElement('P');
        +      const icon = document.createElement('IMG');
        +      type.dataset.type = MarkInfosEnum[this._opts.type].types[i];
        +      icon.dataset.type = type.dataset.type;
        +      icon.src = `/static/img/logo/${MarkInfosEnum[this._opts.type].types[i]}.svg`;
        +      type.innerHTML = window.BeerCrackerz.nls[this._opts.type](`${MarkInfosEnum[this._opts.type].types[i]}Type`);
        +      if (this._opts.types && this._opts.types.indexOf(MarkInfosEnum[this._opts.type].types[i]) !== -1) {
        +        type.classList.add('selected');
        +      }
        +      type.insertBefore(icon, type.firstChild);
        +      types.appendChild(type);
        +      this._evtIds.push(window.Evts.addEvent('click', type, _elementChecked, this));
        +    }
        +    // Mark description
        +    this._description = this._rootElement.querySelector(`#${this._opts.type}-desc`);
        +    // Handle mark modifiers
        +    const modifiers = this._rootElement.querySelector(`#${this._opts.type}-modifiers`);
        +    for (let i = 0; i < MarkInfosEnum[this._opts.type].modifiers.length; ++i) {
        +      const modifier = document.createElement('P');
        +      const icon = document.createElement('IMG');
        +      modifier.dataset.type = MarkInfosEnum[this._opts.type].modifiers[i];
        +      icon.dataset.type = modifier.dataset.type;
        +      icon.src = `/static/img/logo/${MarkInfosEnum[this._opts.type].modifiers[i]}.svg`;
        +      modifier.innerHTML = window.BeerCrackerz.nls[this._opts.type](`${MarkInfosEnum[this._opts.type].modifiers[i]}Modifier`);
        +      if (this._opts.modifiers && this._opts.modifiers.indexOf(MarkInfosEnum[this._opts.type].modifiers[i]) !== -1) {
        +        modifier.classList.add('selected');
        +      }
        +      modifier.insertBefore(icon, modifier.firstChild);
        +      modifiers.appendChild(modifier);
        +      this._evtIds.push(window.Evts.addEvent('click', modifier, _elementChecked, this));
        +    }
        +    // Rating and pricing if any
        +    this._rating = new Rating(this._rootElement.querySelector(`#${this._opts.type}-rating`));
        +    if (this._rootElement.querySelector(`#nls-${this._opts.type}-price`)) {
        +      this._pricing = new Rating(this._rootElement.querySelector(`#${this._opts.type}-pricing`));      
        +    }
        +    this._events();
        +  }
        +
        +
        +  /** @method
        +   * @name _events
        +   * @private
        +   * @memberof WishModal
        +   * @author Arthur Beaulieu
        +   * @since November 2020
        +   * @description <blockquote>This method will listen to any click on the submit button to process the textarea
        +   * content to send it to the backend if needed.</blockquote> **/
        +  _events() {
        +    this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector(`#${this._opts.type}-close`), this.close, this));
        +    this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector(`#${this._opts.type}-close`), this.close, this));
        +    this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector(`#${this._opts.type}-submit`), this.submit, this));
        +    this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector(`#${this._opts.type}-submit`), this.submit, this));
        +  }
        +
        +
        +  submit(event) {
        +    event.preventDefault();
        +    let allowed = true;
        +    this._rootElement.querySelector(`#nls-${this._opts.type}-name`).classList.remove('error');
        +    this._name.classList.remove('error');
        +    this._rootElement.querySelector(`#nls-${this._opts.type}-type`).classList.remove('error');
        +    this._rootElement.querySelector(`#nls-${this._opts.type}-rate`).classList.remove('error');
        +    if (this._rootElement.querySelector(`#nls-${this._opts.type}-price`)) {
        +      this._rootElement.querySelector(`#nls-${this._opts.type}-price`).classList.remove('error');      
        +    }
        +    if (this._name.value === '') {
        +      this._rootElement.querySelector(`#nls-${this._opts.type}-name`).classList.add('error');
        +      this._name.classList.add('error');
        +      window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markNameEmpty'));
        +      allowed = false;
        +    } else if (this.getTypes().length === 0) {
        +      this._rootElement.querySelector(`#nls-${this._opts.type}-type`).classList.add('error');
        +      window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markTypeEmpty'));
        +      allowed = false;
        +    } else if (this._rating.currentRate === -1) {
        +      this._rootElement.querySelector(`#nls-${this._opts.type}-rate`).classList.add('error');
        +      window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markRateEmpty'));
        +      allowed = false;
        +    } else if (this._pricing !== null && this._pricing.currentRate === -1) {
        +      this._rootElement.querySelector(`#nls-${this._opts.type}-price`).classList.add('error');
        +      window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markPriceEmpty'));
        +      allowed = false;
        +    }
        +    return allowed;
        +  }
        +
        +
        +  getTypes() {
        +    const output = [];
        +    const types = this._rootElement.querySelector(`#${this._opts.type}-types`);  
        +    for (let i = 0; i < types.children.length; ++i) {
        +      if (types.children[i].classList.contains('selected')) {
        +        output.push(types.children[i].dataset.type);
        +      }
        +    }
        +    return output;
        +  }
        +
        +
        +  getModifiers() {
        +    const output = [];
        +    const modifiers = this._rootElement.querySelector(`#${this._opts.type}-modifiers`);  
        +    for (let i = 0; i < modifiers.children.length; ++i) {
        +      if (modifiers.children[i].classList.contains('selected')) {
        +        output.push(modifiers.children[i].dataset.type);
        +      }
        +    }
        +    return output;
        +  }
        +
        +
        +}
        +
        +
        +export default MarkModal;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_ui_modal_UserModal.js.html b/front/doc/js_ui_modal_UserModal.js.html new file mode 100644 index 0000000..1e33b74 --- /dev/null +++ b/front/doc/js_ui_modal_UserModal.js.html @@ -0,0 +1,198 @@ + + + + + JSDoc: Source: js/ui/modal/UserModal.js + + + + + + + + + + +
        + +

        Source: js/ui/modal/UserModal.js

        + + + + + + +
        +
        +
        import BaseModal from './BaseModal.js';
        +import VisuHelper from '../VisuHelper.js';
        +import DropElement from '../../utils/DropElement.js';
        +import Utils from '../../utils/Utils.js';
        +
        +
        +class UserModal extends BaseModal {
        +
        +
        +  constructor(options) {
        +    super('user');
        +    this._opts = options;
        +    this._footerCancelButton = null;
        +    this._footerSubmitButton = null;
        +  }
        +
        +
        +  _fillAttributes() {
        +    window.BeerCrackerz.nls.userProfileModal(this._rootElement);
        +    if (window.BeerCrackerz.user.pp) {
        +      this._rootElement.querySelector(`#user-pp`).src = window.BeerCrackerz.user.pp;
        +    }
        +
        +    this._rootElement.querySelector(`#user-name`).innerHTML = window.BeerCrackerz.user.username;
        +    this._rootElement.querySelector(`#user-email`).innerHTML = window.BeerCrackerz.user.email;
        +
        +    new DropElement({
        +      target: this._rootElement.querySelector('#update-pp-wrapper'),
        +      onDrop: this.updateProfilePicture.bind(this)
        +    });
        +    new DropElement({
        +      target: this._rootElement.querySelector('#drop-user-pp'),
        +      onDrop: this.updateProfilePicture.bind(this)
        +    });
        +
        +    // Init modal checkbox state according to local storage preferences
        +    if (Utils.getPreference('map-high-accuracy') === 'true') {
        +      this._rootElement.querySelector('#high-accuracy-toggle').checked = true;
        +    }
        +
        +    if (Utils.getPreference('dark-theme') === 'true') {
        +      this._rootElement.querySelector('#dark-theme-toggle').checked = true;
        +    }
        +
        +    if (Utils.getPreference('startup-help') === 'true') {
        +      this._rootElement.querySelector('#startup-help-toggle').checked = true;
        +    }
        +
        +    if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) {
        +      this._rootElement.querySelector('#debug-toggle').checked = true;
        +    }
        +
        +    const options = this._rootElement.querySelector('#lang-select').getElementsByTagName('option');
        +    const lang = Utils.getPreference('selected-lang');
        +    for (let i = 0; i < options.length; ++i) {
        +      if (options[i].value === lang) {
        +        options[i].selected = 'selected';
        +      }
        +    }
        +
        +    this._events();
        +  }
        +
        +
        +  /** @method
        +   * @name _events
        +   * @private
        +   * @memberof WishModal
        +   * @author Arthur Beaulieu
        +   * @since November 2020
        +   * @description <blockquote>This method will listen to any click on the submit button to process the textarea
        +   * content to send it to the backend if needed.</blockquote> **/
        +  _events() {
        +    this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#high-accuracy-toggle'), window.BeerCrackerz.toggleHighAccuracy, window.BeerCrackerz));
        +    this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#dark-theme-toggle'), VisuHelper.toggleDarkTheme, VisuHelper));
        +    this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#debug-toggle'), VisuHelper.toggleDebug, VisuHelper));
        +    this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#startup-help-toggle'), VisuHelper.toggleStartupHelp, VisuHelper));
        +    this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#lang-select'), window.BeerCrackerz.updateLang, window.BeerCrackerz));
        +    this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#update-pp'), this.updateProfilePicture, this));
        +    this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector('#user-pp'), this.updateProfilePicture, this));
        +    this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector('#user-pp'), this.updateProfilePicture, this));
        +    this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector('#logout'), this.logout, this));
        +    this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector('#logout'), this.logout, this));
        +  }
        +
        +
        +  updateProfilePicture(event) {
        +    if (event.type === 'click') { // Clicked on actual pp
        +      const evt = document.createEvent('MouseEvents');
        +      evt.initEvent('click', true, false);
        +      document.getElementById('update-pp').dispatchEvent(evt);
        +      return;
        +    }
        +
        +    const fileInput = document.getElementById('update-pp');
        +    let files = { files: fileInput.files }; // From input change
        +    if (event.files && event.files.length === 1) { // From drop
        +      files = { files: event.files };
        +    }
        +
        +    if (files.files && files.files.length === 1) {
        +      // Check if file is conform to what's expected
        +      if (files.files[0].size > 2621440) { // 2.5Mo
        +        document.getElementById('update-pp').value = '';
        +        document.getElementById('update-pp').classList.add('error');
        +        document.getElementById('update-pp-error').innerHTML = window.BeerCrackerz.nls.modal('updatePPSizeError');
        +        return;
        +      }
        +
        +      if (FileReader) {
        +        const fr = new FileReader();
        +        fr.onload = () => {
        +          var image = new Image();
        +          image.src = fr.result;
        +          image.onload = () => {
        +            if (image.width < 512 || image.height < 512) {
        +              document.getElementById('update-pp').value = '';
        +              document.getElementById('update-pp').classList.add('error');
        +              document.getElementById('update-pp-error').innerHTML = window.BeerCrackerz.nls.modal('updatePPDimensionError');
        +              return;
        +            } else {
        +              window.Evts.publish('updateProfile', {
        +                image: image,
        +                b64: fr.result
        +              });
        +            }
        +          };
        +        };
        +        fr.readAsDataURL(files.files[0]);
        +      } else {
        +        console.error('Couldnt read file');
        +      }
        +    }
        +  }
        +
        +
        +  logout() {
        +    window.BeerCrackerz.kom.post('api/auth/logout/', null).then(() => {
        +      window.location = '/welcome'
        +    });
        +  }
        +
        +
        +}
        +
        +
        +export default UserModal;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_utils_CustomEvents.js.html b/front/doc/js_utils_CustomEvents.js.html new file mode 100644 index 0000000..47b518f --- /dev/null +++ b/front/doc/js_utils_CustomEvents.js.html @@ -0,0 +1,503 @@ + + + + + JSDoc: Source: js/utils/CustomEvents.js + + + + + + + + + + +
        + +

        Source: js/utils/CustomEvents.js

        + + + + + + +
        +
        +
        import Utils from './Utils.js';
        +
        +
        +class CustomEvents {
        +
        +
        +  /** @summary <h1>JavaScript regular and custom events abstraction</h1>
        +   * @author Arthur Beaulieu
        +   * @since June 2020
        +   * @description <blockquote>The CustomEvents class provides an abstraction of JavaScript event listener, to allow
        +   * easy binding and removing those events. It also provides an interface to register custom events. This class is
        +   * meant to be used on all scopes you need ; module or global. Refer to each public method for detailed features.
        +   * For source code, please go to <a href="https://github.com/ArthurBeaulieu/CustomEvents.js" alt="custom-events-js">
        +   * https://github.com/ArthurBeaulieu/CustomEvents.js</a></blockquote>
        +   * @param {boolean} [debug=false] - Debug flag ; when true, logs will be output in JavaScript console at each event */
        +  constructor(debug = false) {
        +    // Prevent wrong type for debug
        +    if (typeof debug !== 'boolean') {
        +      debug = false;
        +    }
        +    /** @private
        +     * @member {boolean} - Internal logging flag from constructor options, allow to output each event action */
        +    this._debug = debug;
        +    /** @private
        +     * @member {number} - Start the ID incrementer at pseudo random value, used for both regular and custom events */
        +    this._idIncrementor = (Math.floor(Math.random() * Math.floor(256)) * 5678);
        +    /** @private
        +     * @member {any[]} - We store classical event listeners in array of objects containing all their information */
        +    this._regularEvents = [];
        +    /** @private
        +     * @member {object} - We store custom events by name as key, each key stores an Array of subscribed events */
        +    this._customEvents = {};
        +    /** @public
        +     * @member {string} - Component version */
        +    this.version = '1.2.1';
        +  }
        +
        +
        +  /** @method
        +   * @name destroy
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote>CustomEvents destructor. Will remove all event listeners and keys in instance.</blockquote> */
        +  destroy() {
        +    // Debug logging
        +    this._raise('log', 'CustomEvents.destroy');
        +    // Remove all existing eventListener
        +    this.removeAllEvents();
        +    // Delete object attributes
        +    Utils.removeAllObjectKeys(this);
        +  }
        +
        +
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +  /*  --------------------------------------  CLASSIC JS EVENTS OVERRIDE  ------------------------------------------  */
        +  /*                                                                                                                  */
        +  /*  The following methods are made to abstract the event listeners from the JavaScript layer, so you can easily     */
        +  /*  remove them when done using, without bothering with binding usual business for them. 'addEvent/removeEvent'     */
        +  /*  method replace the initial ones. 'removeAllEvents' clears all instance event listeners ; nice for destroy       */
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +
        +
        +  /** @method
        +   * @name addEvent
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote><code>addEvent</code> method abstracts the <code>addEventListener</code> method to easily
        +   * remove it when needed, also to set a custom scope on callback.</blockquote>
        +   * @param {string} eventName - The event name to fire (mousemove, click, context etc.)
        +   * @param {object} element - The DOM element to attach the listener to
        +   * @param {function} callback - The callback function to execute when event is realised
        +   * @param {object} [scope=element] - The event scope to apply to the callback (optional, default to DOM element)
        +   * @param {object|boolean} [options=false] - The event options (useCapture and else)
        +   * @returns {number|boolean} - The event ID to use to manually remove an event, false if arguments are invalid */
        +  addEvent(eventName, element, callback, scope = element, options = false) {
        +    // Debug logging
        +    this._raise('log', `CustomEvents.addEvent: ${eventName} ${element} ${callback} ${scope} ${options}`);
        +    // Missing mandatory arguments
        +    if (eventName === null || eventName === undefined ||
        +      element === null || element === undefined ||
        +      callback === null || callback === undefined) {
        +      this._raise('error', 'CustomEvents.addEvent: Missing mandatory arguments');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory and optional)
        +    const err = () => {
        +      this._raise('error', 'CustomEvents.addEvent: Wrong type for argument');
        +    };
        +    // Test argument validity for further process
        +    if (typeof eventName !== 'string' || typeof element !== 'object' || typeof callback !== 'function') {
        +      err();
        +      return false;
        +    }
        +    if ((scope !== null && scope !== undefined) && typeof scope !== 'object' && typeof scope !== 'function') {
        +      err();
        +      return false;
        +    }
        +    if ((options !== null && options !== undefined) && (typeof options !== 'object' && typeof options !== 'boolean' && typeof options !== 'string' && typeof options !== 'number')) {
        +      err();
        +      return false;
        +    }
        +    // Save scope to callback function, default scope is DOM target object
        +    callback = callback.bind(scope);
        +    // Add event to internal array and keep all its data
        +    this._regularEvents.push({
        +      id: this._idIncrementor,
        +      element: element,
        +      eventName: eventName,
        +      scope: scope,
        +      callback: callback,
        +      options: options
        +    });
        +    // Add event listener with options
        +    element.addEventListener(eventName, callback, options);
        +    // Post increment to return the true event entry id, then update the incrementer
        +    return this._idIncrementor++;
        +  }
        +
        +
        +  /** @method
        +   * @name removeEvent
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote><code>removeEvent</code> method abstracts the <code>removeEventListener</code> method to
        +   * really remove event listeners.</blockquote>
        +   * @param {number} eventId - The event ID to remove listener from. Returned when addEvent is called
        +   * @returns {boolean} - The method status ; true for success, false for non-existing event */
        +  removeEvent(eventId) {
        +    // Debug logging
        +    this._raise('log', `Events.removeEvent: ${eventId}`);
        +    // Missing mandatory arguments
        +    if (eventId === null || eventId === undefined) {
        +      this._raise('error', 'CustomEvents.removeEvent: Missing mandatory arguments');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory)
        +    if (typeof eventId !== 'number') {
        +      this._raise('error', 'CustomEvents.removeEvent: Wrong type for argument');
        +      return false;
        +    }
        +    // Returned value
        +    let statusCode = false; // Not found status code by default (false)
        +    // Iterate over saved listeners, reverse order for proper splicing
        +    for (let i = (this._regularEvents.length - 1); i >= 0 ; --i) {
        +      // If an event ID match in saved ones, we remove it and update saved listeners
        +      if (this._regularEvents[i].id === eventId) {
        +        // Update status code
        +        statusCode = true; // Found and removed event listener status code (true)
        +        this._clearRegularEvent(i);
        +      }
        +    }
        +    // Return with status code
        +    return statusCode;
        +  }
        +
        +
        +  /** @method
        +   * @name removeAllEvents
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote>Clear all event listener registered through this class object.</blockquote>
        +   * @returns {boolean} - The method status ; true for success, false for not removed any event */
        +  removeAllEvents() {
        +    // Debug logging
        +    this._raise('log', 'CustomEvents.removeAllEvents');
        +    // Returned value
        +    let statusCode = false; // Didn't removed any status code by default (false)
        +    // Flag to know if there was any previously stored event listeners
        +    const hadEvents = (this._regularEvents.length > 0);
        +    // Iterate over saved listeners, reverse order for proper splicing
        +    for (let i = (this._regularEvents.length - 1); i >= 0; --i) {
        +      this._clearRegularEvent(i);
        +    }
        +    // If all events where removed, update statusCode to success
        +    if (this._regularEvents.length === 0 && hadEvents) {
        +      // Update status code
        +      statusCode = true; // Found and removed all events listener status code (true)
        +    }
        +    // Return with status code
        +    return statusCode;
        +  }
        +
        +
        +  /** @method
        +   * @name _clearRegularEvent
        +   * @private
        +   * @memberof CustomEvents
        +   * @description <blockquote><code>_clearRegularEvent</code> method remove the saved event listener for a
        +   * given index in regularEvents array range.</blockquote>
        +   * @param {number} index - The regular event index to remove from class attributes
        +   * @return {boolean} - The method status ; true for success, false for not cleared any event */
        +  _clearRegularEvent(index) {
        +    // Debug logging
        +    this._raise('log', `CustomEvents._clearRegularEvent: ${index}`);
        +    // Missing mandatory arguments
        +    if (index === null || index === undefined) {
        +      this._raise('error', 'CustomEvents._clearRegularEvent: Missing mandatory argument');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory)
        +    if (typeof index !== 'number') {
        +      this._raise('error', 'CustomEvents._clearRegularEvent: Wrong type for argument');
        +      return false;
        +    }
        +    // Check if index match an existing event in attributes
        +    if (this._regularEvents[index]) {
        +      // Remove its event listener and update regularEvents array
        +      const evt = this._regularEvents[index];
        +      evt.element.removeEventListener(evt.eventName, evt.callback, evt.options);
        +      this._regularEvents.splice(index, 1);
        +      return true;
        +    }
        +
        +    return false;
        +  }
        +
        +
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +  /*  -------------------------------------------  CUSTOM JS EVENTS  -----------------------------------------------  */
        +  /*                                                                                                                  */
        +  /*  The three following methods (subscribe, unsubscribe, publish) are designed to reference an event by its name    */
        +  /*  and handle as many subscriptions as you want. When subscribing, you get an ID you can use to unsubscribe your   */
        +  /*  event later. Just publish with the event name to callback all its registered subscriptions.                     */
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +
        +
        +  /** @method
        +   * @name subscribe
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote>Subscribe method allow you to listen to an event and react when it occurs.</blockquote>
        +   * @param {string} eventName - Event name (the one to use to publish)
        +   * @param {function} callback - The callback to execute when event is published
        +   * @param {boolean} [oneShot=false] - One shot : to remove subscription the first time callback is fired
        +   * @returns {number|boolean} - The event id, to be used when manually unsubscribing */
        +  subscribe(eventName, callback, oneShot = false) {
        +    // Debug logging
        +    this._raise('log', `CustomEvents.subscribe: ${eventName} ${callback} ${oneShot}`);
        +    // Missing mandatory arguments
        +    if (eventName === null || eventName === undefined ||
        +      callback === null || callback === undefined) {
        +      this._raise('error', 'CustomEvents.subscribe', 'Missing mandatory arguments');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory and optional)
        +    const err = () => {
        +      this._raise('error', 'CustomEvents.subscribe: Wrong type for argument');
        +    };
        +    if (typeof eventName !== 'string' || typeof callback !== 'function') {
        +      err();
        +      return false;
        +    }
        +    if ((oneShot !== null && oneShot !== undefined) && typeof oneShot !== 'boolean') {
        +      err();
        +      return false;
        +    }
        +    // Create event entry if not already existing in the registered events
        +    if (!this._customEvents[eventName]) {
        +      this._customEvents[eventName] = []; // Set empty array for new event subscriptions
        +    }
        +    // Push new subscription for event name
        +    this._customEvents[eventName].push({
        +      id: this._idIncrementor,
        +      name: eventName,
        +      os: oneShot,
        +      callback: callback
        +    });
        +    // Post increment to return the true event entry id, then update the incrementer
        +    return this._idIncrementor++;
        +  }
        +
        +
        +  /** @method
        +   * @name unsubscribe
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote>Unsubscribe method allow you to revoke an event subscription from its string name.</blockquote>
        +   * @param {number} eventId - The subscription id returned when subscribing to an event name
        +   * @returns {boolean} - The method status ; true for success, false for non-existing subscription **/
        +  unsubscribe(eventId) {
        +    // Debug logging
        +    this._raise('log', `CustomEvents.unsubscribe: ${eventId}`);
        +    // Missing mandatory arguments
        +    if (eventId === null || eventId === undefined) {
        +      this._raise('error', 'CustomEvents.unsubscribe: Missing mandatory arguments');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory)
        +    if (typeof eventId !== 'number') {
        +      this._raise('error', 'CustomEvents.unsubscribe: Wrong type for argument');
        +      return false;
        +    }
        +    // Returned value
        +    let statusCode = false; // Not found status code by default (false)
        +    // Save event keys to iterate properly on this._events Object
        +    const keys = Object.keys(this._customEvents);
        +    // Reverse events iteration to properly splice without messing with iteration order
        +    for (let i = (keys.length - 1); i >= 0; --i) {
        +      // Get event subscriptions
        +      const subs = this._customEvents[keys[i]];
        +      // Iterate over events subscriptions to find the one with given id
        +      for (let j = 0; j < subs.length; ++j) {
        +        // In case we got a subscription for this events
        +        if (subs[j].id === eventId) {
        +          // Debug logging
        +          this._raise('log', `CustomEvents.unsubscribe: subscription found\n`, subs[j], `\nSubscription n°${eventId} for ${subs.name} has been removed`);
        +          // Update status code
        +          statusCode = true; // Found and unsubscribed status code (true)
        +          // Remove subscription from event Array
        +          subs.splice(j, 1);
        +          // Remove event name if no remaining subscriptions
        +          if (subs.length === 0) {
        +            delete this._customEvents[keys[i]];
        +          }
        +          // Break since id are unique and no other subscription can be found after
        +          break;
        +        }
        +      }
        +    }
        +    // Return with status code
        +    return statusCode;
        +  }
        +
        +
        +  /** @method
        +   * @name unsubscribeAllFor
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote><code>unsubscribeAllFor</code> method clear all subscriptions registered for given event name.</blockquote>
        +   * @param {string} eventName - The event to clear subscription from
        +   * @returns {boolean} - The method status ; true for success, false for non-existing event **/
        +  unsubscribeAllFor(eventName) {
        +    // Debug logging
        +    this._raise('log', `CustomEvents.unsubscribeAllFor: ${eventName}`);
        +    // Missing mandatory arguments
        +    if (eventName === null || eventName === undefined) {
        +      this._raise('error', 'CustomEvents.unsubscribeAllFor: Missing mandatory arguments');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory and optional)
        +    if (typeof eventName !== 'string') {
        +      this._raise('error', 'CustomEvents.unsubscribeAllFor: Wrong type for argument');
        +      return false;
        +    }
        +    // Returned value
        +    let statusCode = false; // Not found status code by default (false)
        +    // Save event keys to iterate properly on this._events Object
        +    const keys = Object.keys(this._customEvents);
        +    // Iterate through custom event keys to find matching event to remove
        +    for (let i = 0; i < keys.length; ++i) {
        +      if (keys[i] === eventName) {
        +        // Get event subscriptions
        +        const subs = this._customEvents[keys[i]];
        +        // Iterate over events subscriptions to find the one with given id, reverse iteration to properly splice without messing with iteration order
        +        for (let j = (subs.length - 1); j >= 0; --j) {
        +          // Update status code
        +          statusCode = true; // Found and unsubscribed all status code (true)
        +          // Remove subscription from event Array
        +          subs.splice(j, 1);
        +          // Remove event name if no remaining subscriptions
        +          if (subs.length === 0) {
        +            delete this._customEvents[keys[i]];
        +          }
        +        }
        +      }
        +    }
        +    // Return with status code
        +    return statusCode;
        +  }
        +
        +
        +  /** @method
        +   * @name publish
        +   * @public
        +   * @memberof CustomEvents
        +   * @description <blockquote><code>Publish</code> method allow you to fire an event by name and trigger all its subscription by callbacks./blockquote>
        +   * @param {string} eventName - Event name (the one to use to publish)
        +   * @param {object} [data=undefined] - The data object to sent through the custom event
        +   * @returns {boolean} - The method status ; true for success, false for non-existing event **/
        +  publish(eventName, data = null) {
        +    // Debug logging
        +    this._raise('log', `CustomEvents.publish: ${eventName} ${data}`);
        +    // Missing mandatory arguments
        +    if (eventName === null || eventName === undefined) {
        +      this._raise('error', 'CustomEvents.publish: Missing mandatory arguments');
        +      return false;
        +    }
        +    // Prevent wrong type for arguments (mandatory and optional)
        +    if (typeof eventName !== 'string' || (data !== undefined && typeof data !== 'object')) {
        +      this._raise('error', 'CustomEvents.publish: Wrong type for argument');
        +      return false;
        +    }
        +    // Returned value
        +    let statusCode = false; // Not found status code by default (false)
        +    // Save event keys to iterate properly on this._events Object
        +    const keys = Object.keys(this._customEvents);
        +    // Iterate over saved custom events
        +    for (let i = 0; i < keys.length; ++i) {
        +      // If published name match an existing events, we iterate its subscriptions. First subscribed, first served
        +      if (keys[i] === eventName) {
        +        // Update status code
        +        statusCode = true; // Found and published status code (true)
        +        // Get event subscriptions
        +        const subs = this._customEvents[keys[i]];
        +        // Iterate over events subscriptions to find the one with given id
        +        // Reverse subscriptions iteration to properly splice without messing with iteration order
        +        for (let j = (subs.length - 1); j >= 0; --j) {
        +          // Debug logging
        +          this._raise('log', `CustomEvents.publish: fire callback for ${eventName}, subscription n°${subs[j].id}`, subs[j]);
        +          // Fire saved callback
        +          subs[j].callback(data);
        +          // Remove oneShot listener from event entry
        +          if (subs[j].os) {
        +            // Debug logging
        +            this._raise('log', 'CustomEvents.publish: remove subscription because one shot usage is done');
        +            subs.splice(j, 1);
        +            // Remove event name if no remaining subscriptions
        +            if (subs.length === 0) {
        +              delete this._customEvents[keys[i]];
        +            }
        +          }
        +        }
        +      }
        +    }
        +    // Return with status code
        +    return statusCode;
        +  }
        +
        +
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +  /*  --------------------------------------------  COMPONENT UTILS  -----------------------------------------------  */
        +  /*  --------------------------------------------------------------------------------------------------------------- */
        +
        +
        +  /** @method
        +   * @name _raise
        +   * @private
        +   * @memberof CustomEvents
        +   * @description <blockquote>Internal method to abstract console wrapped in debug flag.</blockquote>
        +   * @param {string} level - The console method to call
        +   * @param {string} errorValue - The error value to display in console method **/
        +  _raise(level, errorValue) {
        +    if (this._debug) {
        +      console[level](errorValue);
        +    }
        +  }
        +
        +
        +}
        +
        +
        +export default CustomEvents;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_utils_DropElement.js.html b/front/doc/js_utils_DropElement.js.html new file mode 100644 index 0000000..2c38c8e --- /dev/null +++ b/front/doc/js_utils_DropElement.js.html @@ -0,0 +1,252 @@ + + + + + JSDoc: Source: js/utils/DropElement.js + + + + + + + + + + +
        + +

        Source: js/utils/DropElement.js

        + + + + + + +
        +
        +
        import Utils from './Utils.js';
        +
        +
        +class DropElement {
        +
        +
        +    /** @summary <h1>Make any DOM element drop friendly</h1>
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This class will make any DOM element able to receive drop event. It propose an overlay
        +     * when the target is hovered with a draggable element. It handle both the desktop and the mobile behavior. It must be
        +     * used with a DragElement class for perfect compatibility!</blockquote>
        +     * @param {object} options - The element to drop options
        +     * @param {object} options.target - The element to allow dropping in **/
        +    constructor(options) {
        +      /** @private
        +       * @member {object} - The element to make allow dropping in */
        +      this._target = options.target; // Get given target from the DOM
        +      /** @private
        +       * @member {function} - The callback function to call on each drop event */
        +      this._onDropCB = options.onDrop;
        +      /** @private
        +       * @member {number[]} - The event IDs for all mobile and desktop dropping events */
        +      this._evtIds = [];
        +      /** @private
        +       * @member {number} - This counter helps to avoid enter/leave events to overlap when target has children */
        +      this._movementCounter = 0;
        +      /** @private
        +       * @member {string} - The transparent border that must be added to avoid weird target resize on hover */
        +      this._transparentBorder = '';
        +      // Build DOM elements and subscribe to drag events
        +      this._buildElements();
        +      this._events();
        +    }
        +  
        +  
        +    /** @method
        +     * @name destroy
        +     * @public
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will unsubscribe all drop events and remove all properties.</blockquote> **/
        +    destroy() {
        +//      Utils.clearAllEvents(this._evtIds);
        +      Utils.removeAllObjectKeys(this);
        +    }
        +  
        +  
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +    /*  ---------------------------------  DROPELEMENT INSTANTIATION SEQUENCE  ---------------------------------------  */
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +  
        +  
        +    /** @method
        +     * @name _buildElements
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will define the transparent border style and append this virtual border to the
        +     * target DOM element.</blockquote> **/
        +    _buildElements() {
        +      this._transparentBorder = 'dashed 3px transparent';
        +      this._target.style.border = this._transparentBorder;
        +    }
        +  
        +  
        +    /** @method
        +     * @name _events
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will subscribe to drop events, both for desktop and mobile.</blockquote> **/
        +    _events() {
        +      this._target.addEventListener('dragenter', this._dragEnter.bind(this));
        +      this._target.addEventListener('dragover', this._dragOver.bind(this));
        +      this._target.addEventListener('dragleave', this._dragLeave.bind(this));
        +      this._target.addEventListener('drop', this._drop.bind(this));
        +    }
        +  
        +  
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +    /*  -----------------------------------  DESKTOP DROP EVENTS METHODS  --------------------------------------------  */
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +  
        +  
        +    /** @method
        +     * @name _dragEnter
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will handle the entering of a dragged div over the target DOM element. When
        +     * the target DOM element is hovered, a dashed border is made visible, replacing the transparent one to notify the
        +     * user that the dragged div can be dropped.</blockquote>
        +     * @param {object} event - The mouse event **/
        +    _dragEnter(event) {
        +      this._eventBehavior(event);
        +      ++this._movementCounter;
        +      this._target.style.border = 'dashed 3px #ffa800';
        +    }
        +  
        +  
        +    /** @method
        +     * @name _dragOver
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will handle the dragged div hovering the target DOM element.</blockquote>
        +     * @param {object} event - The mouse event **/
        +    _dragOver(event) {
        +      this._eventBehavior(event);
        +      event.dataTransfer.dropEffect = 'copy';
        +    }
        +  
        +  
        +    /** @method
        +     * @name _dragLeave
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will handle the event that is fired when the hovered div leaves the target
        +     * DOM element. It require the movement counter to be equal to zero to restore the transparent border of the target
        +     * DOM element.</blockquote>
        +     * @param {object} event - The mouse event **/
        +    _dragLeave(event) {
        +      this._eventBehavior(event);
        +      --this._movementCounter;
        +      if (this._movementCounter === 0) {
        +        this._target.style.border = this._transparentBorder;
        +      }
        +    }
        +  
        +  
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +    /*  ------------------------------  MOBILE AND DESKTOP DROP EVENTS METHODS  --------------------------------------  */
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +  
        +  
        +    /** @method
        +     * @name _drop
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will handle the dropping of a DragElement, to properly read the data it holds
        +     * and send it to the drop callback provided in constructor.</blockquote>
        +     * @param {object} event - The mouse or touch event **/
        +    _drop(event) {
        +      this._eventBehavior(event);
        +      this._target.style.border = this._transparentBorder;
        +      this._onDropCB(event.dataTransfer);
        +    }
        +  
        +  
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +    /*  -------------------------------------------  UTILS METHODS  --------------------------------------------------  */
        +    /*  --------------------------------------------------------------------------------------------------------------- */
        +  
        +  
        +    /** @method
        +     * @name _eventBehavior
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will prevent the default behavior of given event, and will stop its
        +     * propagation.</blockquote>
        +     * @param {object} event - The mouse or touch event **/
        +    _eventBehavior(event) {
        +      event.preventDefault();
        +      event.stopPropagation();
        +    }
        +  
        +  
        +    /** @method
        +     * @name _isTouchEventInTarget
        +     * @private
        +     * @memberof DropElement
        +     * @author Arthur Beaulieu
        +     * @since December 2020
        +     * @description <blockquote>This method will compare a touch point to the target position and return true if the
        +     * touch point is inside the target DOM element.</blockquote>
        +     * @param {object} touchPosition - The touch event
        +     * @return {boolean} Do the touch point is included in the target DOM element **/
        +    _isTouchEventInTarget(touchPosition) {
        +      const rect = this._target.getBoundingClientRect();
        +      const inAxisX = touchPosition.pageX >= rect.x && (touchPosition.pageX <= rect.x + rect.width);
        +      const inAxisY = touchPosition.pageY >= rect.y && (touchPosition.pageY <= rect.y + rect.height);
        +      return (inAxisX && inAxisY);
        +    }
        +  
        +  
        +  }
        +  
        +  
        +  export default DropElement;
        +  
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/front/doc/js_utils_Utils.js.html b/front/doc/js_utils_Utils.js.html new file mode 100644 index 0000000..906aa98 --- /dev/null +++ b/front/doc/js_utils_Utils.js.html @@ -0,0 +1,342 @@ + + + + + JSDoc: Source: js/utils/Utils.js + + + + + + + + + + +
        + +

        Source: js/utils/Utils.js

        + + + + + + +
        +
        +
        /**
        + * @class
        + * @static
        + * @public
        +**/
        +class Utils {
        +
        +
        +  /**
        +   * @method
        +   * @name stripDom
        +   * @public
        +   * @static
        +   * @memberof Utils
        +   * @author Arthur Beaulieu
        +   * @since November 2022
        +   * @description
        +   * <blockquote>
        +   * From a given string/number input, this method will strip all unnecessary
        +   * characters and will only retrun the text content as a string.
        +   * </blockquote>
        +   * @param {String|Number} html - The html string to strip
        +   * @return {String} The stripped text content, empty string on error
        +   **/
        +  static stripDom(html) {
        +    // Not accepting empty or not string/number
        +    if (!html || (typeof html !== 'string' && typeof html !== 'number')) {
        +      return '';      
        +    }
        +
        +    const doc = new DOMParser().parseFromString(html, 'text/html');
        +    return doc.body.textContent || '';
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name replaceString
        +   * @public
        +   * @static
        +   * @memberof Utils
        +   * @author Arthur Beaulieu
        +   * @since November 2022
        +   * @description
        +   * <blockquote>
        +   * Replace a given string in an HTML element with another. 
        +   * </blockquote>
        +   * @param {Element} element - The DOM element to replace string in
        +   * @param {String} string - The string to be replaced
        +   * @param {String} value - The value to apply to the replaced string
        +   * @return {Boolean} The success status of the replace action
        +   **/
        +  static replaceString(element, string, value) {
        +    if (!element || !element.innerHTML || !string || typeof string !== 'string' || !value || typeof value !== 'string') {
        +      return false;
        +    }
        +
        +    element.innerHTML = element.innerHTML.replace(string, value);
        +    return true;
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name getDistanceBetweenCoords
        +   * @public
        +   * @static
        +   * @memberof Utils
        +   * @author Arthur Beaulieu
        +   * @since November 2022
        +   * @description
        +   * <blockquote>
        +   * Compute the distance in meters between two points given in [Lat, Lng] arrays. 
        +   * </blockquote>
        +   * @param {Array} from - The first point lat and lng array
        +   * @param {Array} to - The second point lat and lng array
        +   * @return {Number} A floating number, the distance between two points given in meters
        +   **/
        +  static getDistanceBetweenCoords(from, to) {
        +    // Generic argument testing
        +    if (!from || !to || !Array.isArray(from) || !Array.isArray(to)) {
        +      return -1;
        +    }
        +    // From input array testing
        +    if (from.length !== 2 || typeof from[0] !== 'number' || typeof from[1] !== 'number') {
        +      return -1;
        +    }
        +    // To input array testing
        +    if (to.length !== 2 || typeof to[0] !== 'number' || typeof to[1] !== 'number') {
        +      return -1;
        +    }
        +    // Return distance in meters
        +    const lat1 = (from[0] * Math.PI) / 180;
        +    const lon1 = (from[1] * Math.PI) / 180;
        +    const lat2 = (to[0] * Math.PI) / 180;
        +    const lon2 = (to[1] * Math.PI) / 180;
        +    // Delta between coords to compute output distance
        +    const deltaLat = lat2 - lat1;
        +    const deltaLon = lon2 - lon1;
        +    const a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2);
        +    const c = 2 * Math.asin(Math.sqrt(a));
        +    return (c * 6371000); // Earth radius in meters
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name precisionRound
        +   * @public
        +   * @memberof Utils
        +   * @author Arthur Beaulieu
        +   * @since September 2018
        +   * @description
        +   * <blockquote>
        +   * Do a Math.round with a given precision (ie amount of integers after the coma). 
        +   * </blockquote>
        +   * @param {Nunmber} value - The value to precisely round (> 0)
        +   * @param {Number} precision - The number of integers after the coma (> 0)
        +   * @return {Number} - The rounded value 
        +   **/
        +  static precisionRound(value, precision) {
        +    if (typeof value !== 'number' || typeof precision !== 'number') {
        +      return -1;
        +    }
        +    const multiplier = Math.pow(10, precision || 0);
        +    return Math.round(value * multiplier) / multiplier;
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name setDefaultPreferences
        +   * @public
        +   * @static
        +   * @memberof Utils
        +   * @author Arthur Beaulieu
        +   * @since November 2022
        +   * @description
        +   * <blockquote>
        +   * Analyze preferences and fallback to default values if preferences doesn't exists.
        +   * </blockquote>
        +   **/
        +  static setDefaultPreferences() {
        +    if (Utils.getPreference('poi-show-spot') === null) {
        +      Utils.setPreference('poi-show-spot', true);
        +    }
        +
        +    if (Utils.getPreference('poi-show-shop') === null) {
        +      Utils.setPreference('poi-show-shop', true);
        +    }
        +
        +    if (Utils.getPreference('poi-show-bar') === null) {
        +      Utils.setPreference('poi-show-bar', true);
        +    }
        +
        +    if (Utils.getPreference('poi-show-circle') === null) {
        +      Utils.setPreference('poi-show-circle', true);
        +    }
        +
        +    if (Utils.getPreference('poi-show-label') === null) {
        +      Utils.setPreference('poi-show-label', true);
        +    }
        +
        +    if (Utils.getPreference('map-plan-layer') === null) {
        +      Utils.setPreference('map-plan-layer', 'Plan OSM');
        +    }
        +
        +    if (Utils.getPreference('selected-lang') === null) {
        +      Utils.setPreference('selected-lang', 'en');
        +    }
        +
        +    if (Utils.getPreference('app-debug') === null) {
        +      Utils.setPreference('app-debug', false);
        +    }
        +
        +    if (Utils.getPreference('map-high-accuracy') === null) {
        +      Utils.setPreference('map-high-accuracy', false);
        +    }
        +
        +    if (Utils.getPreference('map-center-on-user') === null) {
        +      Utils.setPreference('map-center-on-user', false);
        +    }
        +    
        +    if (Utils.getPreference('dark-theme') === null) {
        +      Utils.setPreference('dark-theme', true);
        +    }
        +
        +    if (Utils.getPreference('startup-help') === null) {
        +      Utils.setPreference('startup-help', true);
        +    }
        +  }
        +
        +
        +  /**
        +   * @method
        +   * @name formatMarker
        +   * @public
        +   * @memberof BeerCrackerz
        +   * @author Arthur Beaulieu
        +   * @since February 2022
        +   * @description
        +   * <blockquote>
        +   * This method formats a mark so it can be parsed using JSON.parse 
        +   * in order to be later stored in database.
        +   * </blockquote>
        +   * @param {Object} mark - The mark options to format for server communication
        +   * @return {Object} The formatted mark
        +   **/
        +  static formatMarker(mark) {
        +    // Mandatory arguments
        +    if (!mark || !mark.name || !mark.types || !mark.lat || !mark.lng) {
        +      return null;
        +    }
        +    // Mandatory arguments proper types
        +    if (typeof mark.name !== 'string' || !Array.isArray(mark.types) || typeof mark.lat !== 'number' || typeof mark.lng !== 'number') {
        +      return null;
        +    }
        +    // Only return if types aren't all strings
        +    for (let i = 0; i < mark.types.length; ++i) {
        +      if (typeof mark.types[i] !== 'string') {
        +        return null;
        +      }
        +    }
        +    // Only return if description is not properly formated
        +    if (mark.description && typeof mark.description !== 'string') {
        +      return null;
        +    }
        +    // Only return if modifiers are not properly formated
        +    if (mark.modifiers) {
        +      if (!Array.isArray(mark.modifiers)) {
        +        return null;
        +      }
        +
        +      for (let i = 0; i < mark.modifiers.length; ++i) {
        +        if (typeof mark.modifiers[i] !== 'string') {
        +          return null;
        +        }
        +      }
        +    }
        +    // Only return if rate is not a number or not between 0 and 4
        +    if (mark.rate && typeof mark.rate !== 'number' || mark.rate < 0 || mark.rate > 4) {
        +      return null;
        +    }
        +    // Only return if price is not a number or not between 0 and 2
        +    if (mark.price && typeof mark.price !== 'number' || mark.price < 0 || mark.price > 2) {
        +      return null;
        +    }
        +    // Finally return formatted mark
        +    return {
        +      name: mark.name,
        +      types: mark.types,
        +      lat: mark.lat,
        +      lng: mark.lng,
        +      description: mark.description,
        +      modifiers: mark.modifiers,
        +      rate: mark.rate,
        +      price: mark.price
        +    };
        +  }
        +
        +
        +  static removeAllObjectKeys(obj) {
        +    if (!obj || typeof obj !== 'object' || Array.isArray(obj)) {
        +      return false;
        +    }
        +
        +    Object.keys(obj).forEach(key => {
        +      delete obj[key];
        +    });
        +
        +    return true;
        +  }
        +
        +
        +  /* Preference get set (DEPRECATED, will be mgrated with user pref when ready) */
        +
        +
        +  static getPreference(pref) {
        +    return localStorage.getItem(pref) || null;
        +  }
        +
        +
        +  static setPreference(pref, value) {
        +    localStorage.setItem(pref, value);
        +  }
        +
        +
        +}
        +
        +
        +export default Utils;
        +
        +
        +
        + + + + +
        + + + +
        + +
        + Documentation generated by JSDoc 4.0.0 on Mon Jan 16 2023 21:18:23 GMT+0100 (Central European Standard Time) +
        + + + + + diff --git a/doc/beercrackerz/0.0.1/scripts/linenumber.js b/front/doc/scripts/linenumber.js similarity index 100% rename from doc/beercrackerz/0.0.1/scripts/linenumber.js rename to front/doc/scripts/linenumber.js diff --git a/doc/beercrackerz/0.0.1/scripts/prettify/Apache-License-2.0.txt b/front/doc/scripts/prettify/Apache-License-2.0.txt similarity index 100% rename from doc/beercrackerz/0.0.1/scripts/prettify/Apache-License-2.0.txt rename to front/doc/scripts/prettify/Apache-License-2.0.txt diff --git a/doc/beercrackerz/0.0.1/scripts/prettify/lang-css.js b/front/doc/scripts/prettify/lang-css.js similarity index 100% rename from doc/beercrackerz/0.0.1/scripts/prettify/lang-css.js rename to front/doc/scripts/prettify/lang-css.js diff --git a/doc/beercrackerz/0.0.1/scripts/prettify/prettify.js b/front/doc/scripts/prettify/prettify.js similarity index 100% rename from doc/beercrackerz/0.0.1/scripts/prettify/prettify.js rename to front/doc/scripts/prettify/prettify.js diff --git a/doc/beercrackerz/0.0.1/styles/jsdoc-default.css b/front/doc/styles/jsdoc-default.css similarity index 100% rename from doc/beercrackerz/0.0.1/styles/jsdoc-default.css rename to front/doc/styles/jsdoc-default.css diff --git a/doc/beercrackerz/0.0.1/styles/prettify-jsdoc.css b/front/doc/styles/prettify-jsdoc.css similarity index 100% rename from doc/beercrackerz/0.0.1/styles/prettify-jsdoc.css rename to front/doc/styles/prettify-jsdoc.css diff --git a/doc/beercrackerz/0.0.1/styles/prettify-tomorrow.css b/front/doc/styles/prettify-tomorrow.css similarity index 100% rename from doc/beercrackerz/0.0.1/styles/prettify-tomorrow.css rename to front/doc/styles/prettify-tomorrow.css diff --git a/front/src/BeerCrackerz.js b/front/src/BeerCrackerz.js new file mode 100644 index 0000000..6258d7a --- /dev/null +++ b/front/src/BeerCrackerz.js @@ -0,0 +1,939 @@ +import './BeerCrackerz.scss'; +import Kom from './js/core/Kom.js'; +import LangManager from './js/core/LangManager.js'; + +import ZoomSlider from './js/ui/component/ZoomSlider.js'; +import Notification from './js/ui/component/Notification.js'; +import VisuHelper from './js/ui/VisuHelper.js'; +import MarkPopup from './js/ui/MarkPopup'; +import ModalFactory from './js/ui/ModalFactory'; + +import CustomEvents from './js/utils/CustomEvents.js'; +import Utils from './js/utils/Utils.js'; +import AccuracyEnum from './js/utils/enums/AccuracyEnum.js'; +import ClustersEnum from './js/utils/enums/ClusterEnum.js'; +import ColorEnum from './js/utils/enums/ColorEnum.js'; +import ProvidersEnum from './js/utils/enums/ProviderEnum.js'; +import MapEnum from './js/utils/enums/MapEnum.js'; + + +window.VERSION = '0.1.0'; +window.Evts = new CustomEvents(); + + +/** + * @class + * @constructor + * @public +**/ +class BeerCrackerz { + + + /** + * @summary The BeerCrackerz main component + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * This component handles the whole BeerCrackerz app. It includes the map manipulation, + * the geolocation API to update the user position and process any map events that are + * relevant to an UX stand point. For more information, please consult the application + * description page at https://about.beercrackerz.org/ + *
        + **/ + constructor() { + /** + * The core Leaflet.js map + * @type {Object} + * @private + **/ + this._map = null; + /** + * The zoom slider handler + * @type {Object} + * @private + **/ + this._zoomSlider = null; + /** + * The notification handler + * @type {Object} + * @private + **/ + this._notification = null; + /** + * The user object holds everything useful to ensure a proper session + * @type {Object} + * @private + **/ + this._user = { + lat: 48.853121540141096, // Default lat to Paris Notre-Dame latitude + lng: 2.3498955769881156, // Default lng to Paris Notre-Dame longitude + accuracy: 0, // Accuracy in meter given by geolocation API + marker: null, // The user marker on map + circle: null, // The accuracy circle around the user marker + range: null, // The range in which user can add a new marker + color: ColorEnum.user, // The color to use for circle (match the user marker color) + id: -1, + username: '' + }; + /** + * The stored marks for spots, shops and bars + * @type {Object} + * @private + **/ + this._marks = { + spot: [], + shop: [], + bar: [] + }; + /** + * The stored clusters for markers, see Leaflet.markercluster plugin + * @type {Object} + * @private + **/ + this._clusters = { + spot: {}, + shop: {}, + bar: {} + }; + /** + * The temporary marker for new marks only + * @type {Object} + * @private + **/ + this._newMarker = null; + + this._popupOpened = false; + /** + * The debug DOM object + * @type {Object} + * @private + **/ + this.debugElement = null; + /** + * ID for geolocation watch callback + * @type {Number} + * @private + **/ + this._watchId = null; + /** + * Flag to know if a zoom action is occuring on map + * @type {Boolean} + * @private + **/ + this._isZooming = false; + /** + * Whether the user is double clicking the map + * @type {Boolean} + * @private + **/ + this._dbClick = false; + /** + * The timeout id for double click detection + * @type {Number} + * @private + **/ + this._dbClickTimeoutId = -1; + /** + * The communication manager to process all server call + * @type {Object} + * @private + **/ + this._kom = new Kom(); + /** + * The LangManager must be instantiated to handle nls accross the app + * @type {Object} + * @private + **/ + // The BeerCrackerz app is only initialized once nls are set up + this._lang = new LangManager(); + // Start app initialization + this._init(); + } + + + // ======================================================================== // + // ----------------- Application initialization sequence ------------------ // + // ======================================================================== // + + + /** + * @method + * @name _init + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _init() method is designed to properly configure the user session, according + * to its saved preferences and its position. It first build the debug interface, + * then loads the user preferences, then create the map and finally, events are listened. + *
        + **/ + _init() { + this._notification = new Notification(); + this._initUser() + .then(this._initPreferences.bind(this)) + .then(this._initGeolocation.bind(this)) + .then(this._initMap.bind(this)) + .then(this._initMarkers.bind(this)) + .then(this._initEvents.bind(this)) + .then(this._startSession.bind(this)); + } + + + /** + * @method + * @name _initUser + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _init() method initialize the user object according to its information + * and statistic so the UI can be properly built. + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initUser() { + return new Promise((resolve, reject) => { + this._kom.get('/api/user/me/').then(user => { + this._user.id = user.id; + this._user.username = user.username; + this._user.email = user.email; + this._user.pp = user.profilePicture; + this._user.isActive = user.isActive; + this._user.isStaff = user.isStaff; + resolve(); + }).catch(reject); + }); + } + + + /** + * @method + * @name _initPreferences + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _initPreferences() will initialize user preference if they are not set yet, + * it will also update the UI according to user preferences ; debug DOM visible, + * update the command classList for selected ones. + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initPreferences() { + return new Promise((resolve, reject) => { + // If no pref, set fallbacks + Utils.setDefaultPreferences(); + // Update icon class if center on preference is set to true + if (Utils.getPreference('map-center-on-user') === 'true') { + document.getElementById('center-on').classList.add('lock-center-on'); + } + // Replace dark-theme class with light-theme class on body + if (Utils.getPreference('dark-theme') === 'false') { + document.body.classList.remove('dark-theme'); + document.body.classList.add('light-theme'); + } + // Update LangManager with pref language + this.nls.updateLang(Utils.getPreference('selected-lang')).then(() => { + this.debugElement = VisuHelper.initDebugUI(); + // Create and append debug UI with proper nls settings + if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) { + window.DEBUG = true; // Ensure to set global flag if preference comes from local storage + Utils.setPreference('app-debug', true); // Ensure to set local storage preference if debug flag was added to the url + VisuHelper.addDebugUI(); + } + resolve(); + }).catch(reject); + }); + } + + + /** + * @method + * @name _initGeolocation + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _initGeolocation() method will request from browser the location authorization. + * Once granted, an event listener is set on any position update, so it can update the + * map state and the markers position. This method can be called again, only if the + * geolocation watch has been cleared ; for example when updating the accuracy options. + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initGeolocation() { + return new Promise(resolve => { + if ('geolocation' in navigator) { + const options = (Utils.getPreference('map-high-accuracy') === 'true') ? AccuracyEnum.high : AccuracyEnum.optimized; + this._watchId = navigator.geolocation.watchPosition(position => { + // Update saved user position + this._user.lat = position.coords.latitude; + this._user.lng = position.coords.longitude; + this._user.accuracy = position.coords.accuracy; + // Only draw marker if map is already created + if (this._map) { + VisuHelper.drawUserMarker(); + // Update map position if focus lock is active + if (Utils.getPreference('map-center-on-user') === 'true' && !this._isZooming) { + this._map.setView(this._user); + } + // Updating debug info + VisuHelper.updateDebugUI(); + } + resolve(); + }, resolve, options); + } else { + this.notification.raise(this.nls.notif('geolocationError')); + resolve(); + } + }); + } + + + /** + * @method + * @name _initMap + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite), + * add scale control, remove zoom control and set map bounds. + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initMap() { + return new Promise(resolve => { + // Use main div to inject OSM into + this._map = window.L.map('beer-crakerz-map', { + zoomControl: false, + }).setView([this._user.lat, this._user.lng], 18); + // Add meter and feet scale on map + window.L.control.scale().addTo(this._map); + // Place user marker on the map + VisuHelper.drawUserMarker(); + // Add OSM credits to the map next to leaflet credits + const osm = ProvidersEnum.planOsm; + const esri = ProvidersEnum.satEsri; + // Prevent panning outside of the world's edge + this._map.setMaxBounds(MapEnum.mapBounds); + // Add layer group to interface + const baseMaps = {}; + baseMaps[`

        ${this.nls.map('planLayerOSM')}

        `] = osm; + baseMaps[`

        ${this.nls.map('satLayerEsri')}

        `] = esri; + // Append layer depending on user preference + if (Utils.getPreference('map-plan-layer')) { + switch (Utils.getPreference('map-plan-layer')) { + case this.nls.map('planLayerOSM'): + osm.addTo(this._map); + break; + case this.nls.map('satLayerEsri'): + esri.addTo(this._map); + break; + default: + osm.addTo(this._map); + break; + } + } else { // No saved pref, fallback on OSM base map + osm.addTo(this._map); + } + // Add layer switch radio on bottom right of the map + window.L.control.layers(baseMaps, {}, { position: 'bottomright' }).addTo(this._map); + // Init zoom slider when map has been created + this._zoomSlider = new ZoomSlider(this._map); + resolve(); + }); + } + + + /** + * @method + * @name _initMarkers + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _initEvents() method will initialize all saved marker into the map. + * Markers must be retrieved from server with a specific format to ensure it works + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initMarkers() { + return new Promise(resolve => { + // Init map clusters for marks to be displayed (disable clustering at opened popup zoom level) + this._clusters.spot = ClustersEnum.spot; + this._clusters.shop = ClustersEnum.shop; + this._clusters.bar = ClustersEnum.bar; + + if (Utils.getPreference(`poi-show-spot`) === 'true') { + this._map.addLayer(this._clusters.spot); + } + if (Utils.getPreference(`poi-show-shop`) === 'true') { + this._map.addLayer(this._clusters.shop); + } + if (Utils.getPreference(`poi-show-bar`) === 'true') { + this._map.addLayer(this._clusters.bar); + } + + const iterateMarkers = mark => { + const popup = new MarkPopup(mark, dom => { + mark.dom = dom; + mark.marker = VisuHelper.addMark(mark); + mark.popup = popup; + this._marks[mark.type].push(mark); + this._clusters[mark.type].addLayer(mark.marker); + }); + }; + + this._kom.getSpots().then(spots => { + for (let i = 0; i < spots.length; ++i) { + iterateMarkers(spots[i]); + } + }); + + this._kom.getShops().then(shops => { + for (let i = 0; i < shops.length; ++i) { + iterateMarkers(shops[i]); + } + }); + + this._kom.getBars().then(bars => { + for (let i = 0; i < bars.length; ++i) { + iterateMarkers(bars[i]); + } + }); + + resolve(); + }); + } + + + /** + * @method + * @name _initEvents + * @private + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The _initEvents() method will listen to all required events to manipulate the map. Those events + * are both for commands and for map events (click, drag, zoom and layer change). + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initEvents() { + return new Promise(resolve => { + // Subscribe to click event on map to react + this._map.on('click', this.mapClicked.bind(this)); + // Map is dragged by user mouse/finger + this._map.on('drag', () => { + // Constrain pan to the map bounds + this._map.panInsideBounds(MapEnum.mapBounds, { animate: true }); + // Disable lock focus if user drags the map + if (Utils.getPreference('map-center-on-user') === 'true') { + VisuHelper.toggleFocusLock(); + } + }); + // Map events + this._map.on('zoomstart', () => { + this._isZooming = true; + }); + this._map.on('zoomend', () => { + this._isZooming = false; + // Updating debug info + VisuHelper.updateDebugUI(); + }); + this._map.on('baselayerchange', event => { + Utils.setPreference('map-plan-layer', Utils.stripDom(event.name)); + }); + + // Update map view for big popups + this._map.on('popupopen', event => { + const px = this._map.project(event.target._popup._latlng); + px.y -= event.target._popup._container.clientHeight / 2; + this._map.flyTo(this._map.unproject(px), this._map.getZoom()); + this._popupOpened = true; + this._zoomSlider.hide(); + }); + + // Clustering events + this._clusters.spot.on('clusterclick', this._zoomSlider.hide.bind(this._zoomSlider)); + this._clusters.shop.on('clusterclick', this._zoomSlider.hide.bind(this._zoomSlider)); + this._clusters.bar.on('clusterclick', this._zoomSlider.hide.bind(this._zoomSlider)); + this._clusters.spot.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'spot')); + this._clusters.shop.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'shop')); + this._clusters.bar.on('animationend', VisuHelper.checkClusteredMark.bind(this, 'bar')); + // Command events + window.Evts.addEvent('click', document.getElementById('user-profile'), this.userProfile, this); + window.Evts.addEvent('click', document.getElementById('hide-show'), this.hidShowMenu, this); + window.Evts.addEvent('click', document.getElementById('center-on'), VisuHelper.toggleFocusLock, this); + // Bus events + window.Evts.subscribe('addMark', this.addMark.bind(this)); // Event from addMarkPopup + window.Evts.subscribe('onMarkAdded', this._onMarkAdded.bind(this)); // Event from MarkPopup + window.Evts.subscribe('deleteMark', this.deleteMark.bind(this)); // Event from MarkPopup + window.Evts.subscribe('onMarkDeleted', this._onMarkDeleted.bind(this)); // User confirmed the mark deletion + window.Evts.subscribe('editMark', this.editMark.bind(this)); // Event from MarkPopup + window.Evts.subscribe('onMarkEdited', this._onMarkEdited.bind(this)); // User confirmed the mark edition + window.Evts.subscribe('updateProfile', this.updateProfilePicture.bind(this)); // Event from user modal + window.Evts.subscribe('onProfilePictureUpdated', this._onProfilePictureUpdated.bind(this)); // Event from update pp modal + + window.Evts.subscribe('centerOn', VisuHelper.centerOn.bind(VisuHelper)); + + resolve(); + }); + } + + + _startSession() { + if (Utils.getPreference(`startup-help`) === 'true') { + this._modal = ModalFactory.build('StartupHelp'); + } else { + this.notification.raise(this.nls.notif('welcomeBack')); + } + } + + + // ======================================================================== // + // ------------------------- Toggle for map items ------------------------- // + // ======================================================================== // + + + /** + * @method + * @name toggleHighAccuracy + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The toggleHighAccuracy() method will, depending on user preference, update the + * geolocation accuracy between optimized and high. The high settings might cause + * more memory and processing consumption, but gives better results. It will clear + * any previous position watch on the geolocation API so it can subscribe a new one + * with the new accuracy parameters (see Utils for values) + *
        + **/ + toggleHighAccuracy() { + const high = !(Utils.getPreference('map-high-accuracy') === 'true'); + Utils.setPreference('map-high-accuracy', high); + navigator.geolocation.clearWatch(this._watchId); + this._initGeolocation().then(VisuHelper.updateDebugUI.bind(VisuHelper)); + } + + + updateLang(event) { + Utils.setPreference('selected-lang', event.target.value); + this.nls.updateLang(Utils.getPreference('selected-lang')).then(() => { + if (this._modal) { + this._modal.close(null, true); + VisuHelper.updateDebugUI(); + setTimeout(this.userProfile.bind(this), 200); + } else { // Lang update from elsewhere than user modal + window.location.reload(); + } + }); + } + + + // ======================================================================== // + // -------------------------- Map interaction ----------------------------- // + // ======================================================================== // + + + /** + * @method + * @name mapClicked + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The mapClicked() method is the callback used when the user clicked on the Leaflet.js map + *
        + * @param {Event} event The click event + **/ + mapClicked(event) { + if (!this._dbClick) { + // On first click, we activate double click detection + // If no registered click in 300mx, handle standard click + this._dbClick = true; + this._dbClickTimeoutId = setTimeout(() => { + this._dbClick = false; + if (this._newMarker && this._newMarker.popupClosed) { // Avoid to open new marker right after popup closing + this._newMarker = null; + this._popupOpened = false; + } else if (this._popupOpened === true) { // Do not open new mark if popup previously opened + this._popupOpened = false; + } else if (this._newMarker === null || !this._newMarker.isBeingDefined) { // Check for new mark + // Only create new marker if click is in range to add a mark + const distance = Utils.getDistanceBetweenCoords([this._user.lat, this._user.lng], [event.latlng.lat, event.latlng.lng]); + if (distance < MapEnum.newMarkRange) { // In range to create new mark + this.addMarkPopup(event.latlng); + this._newMarker.openPopup(); + } else if (this._map.getZoom() >= 10) { // Avoid poluting UI when strong dezoom applied + this.notification.raise(this.nls.notif('newMarkerOutside')); + } + } + }, 200); + } else { + // Second click close enough to the previous one to be qualified double click + this._dbClick = false; + clearTimeout(this._dbClickTimeoutId); + } + } + + + // ======================================================================== // + // ----------------------- Marker manipulation ---------------------------- // + // ======================================================================== // + + + addMarkPopup(options) { + const dom = { + wrapper: document.createElement('DIV'), + title: document.createElement('P'), + spot: document.createElement('BUTTON'), + shop: document.createElement('BUTTON'), + bar: document.createElement('BUTTON'), + }; + // Update class and inner HTMl content according to user's nls + dom.wrapper.className = 'new-poi'; + dom.title.innerHTML = this.nls.map('newTitle'); + dom.spot.innerHTML = this.nls.map('newSpot'); + dom.shop.innerHTML = this.nls.map('newShop'); + dom.bar.innerHTML = this.nls.map('newBar'); + // Atach data type to each button (to be used in clicked callback) + dom.spot.dataset.type = 'spot'; + dom.shop.dataset.type = 'shop'; + dom.bar.dataset.type = 'bar'; + // DOM chaining + dom.wrapper.appendChild(dom.title); + dom.wrapper.appendChild(dom.spot); + dom.wrapper.appendChild(dom.shop); + dom.wrapper.appendChild(dom.bar); + // Update popup content with DOM elements + options.dom = dom.wrapper; + // Create temporary mark with wrapper content and open it to offer user the creation menu + this._newMarker = VisuHelper.addMark(options); + options.marker = this._newMarker; // Attach marker to option so it can be manipulated in clicked callbacks + // Callback on button clicked (to open modal and define a new mark) + const _prepareNewMark = e => { + this._newMarker.isBeingDefined = true; + this._newMarker.closePopup(); + options.type = e.target.dataset.type; + window.Evts.publish('addMark', options); + }; + // Buttons click events + dom.spot.addEventListener('click', _prepareNewMark); + dom.shop.addEventListener('click', _prepareNewMark); + dom.bar.addEventListener('click', _prepareNewMark); + // Listen to clicks outside of popup to close new mark + this._newMarker.on('popupclose', () => { + if (!this._newMarker.isBeingDefined) { + this._newMarker.popupClosed = true; + this._newMarker.removeFrom(this.map); + } + }); + } + + + addMark(options) { + this._modal = ModalFactory.build('AddMark', options); + } + + + _onMarkAdded(options) { + const popup = new MarkPopup(options, dom => { + options.dom = dom; + options.marker = VisuHelper.addMark(options); // Create final marker + options.popup = popup; + // Save new marker in local storage, later to be sent to the server + this._kom[`${options.type}Created`](Utils.formatMarker(options)).then(data => { + // Update marker data to session memory + options.id = data.id; + options.name = data.name; + options.description = data.description; + options.lat = data.lat; + options.lng = data.lng; + // Save marke in marks and clusters for the map + this._marks[options.type].push(options); + this._clusters[options.type].addLayer(options.marker); + // Notify user that new marker has been saved + this.notification.raise(this.nls.notif(`${options.type}Added`)); + // Clear new marker to let user add other stuff + this._newMarker = null; + }).catch(() => { + this.notification.raise(this.nls.notif(`${options.type}NotAdded`)); + }); + }); + } + + + /** + * @method + * @name deleteMark + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since February 2022 + * @description + *
        + * This method will delete a mark after prompting the user if he trully wants to + *
        + * @param {Object} options The mark options to delete + **/ + deleteMark(options) { + this._map.closePopup(); + this._modal = ModalFactory.build('DeleteMark', options); + } + + + _onMarkDeleted(options) { + // Iterate through marks to find matching one (by coord as marks coordinates are unique) + const marks = this._marks[options.type]; + for (let i = 0; i < marks.length; ++i) { + // We found, remove circle, label and marker from map/clusters + if (options.lat === marks[i].lat && options.lng === marks[i].lng) { + // Send data to server + this._kom[`${options.type}Deleted`](marks[i].id, Utils.formatMarker(marks[i])).then(() => { + VisuHelper.removeMarkDecoration(marks[i]); + this._clusters[options.type].removeLayer(marks[i].marker); + marks.splice(i, 1); + // Update internal marks array + this._marks[options.type] = marks; + // Notify user through UI that marker has been successfully deleted + this.notification.raise(this.nls.notif(`${options.type}Deleted`)); + }).catch(() => { + this.notification.raise(this.nls.notif(`${options.type}NotDeleted`)); + }); + break; + } + } + } + + + /** + * @method + * @name editMark + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since February 2022 + * @description + *
        + * This method will open a mark edition modal + *
        + * @param {Object} options The mark options to edit + **/ + editMark(options) { + this._map.closePopup(); + this._modal = ModalFactory.build('EditMark', options); + } + + + _onMarkEdited(options) { + // Iterate through marks to find matching one (by coord as marks coordinates are unique) + for (let i = 0; i < this._marks[options.type].length; ++i) { + // We found, remove circle, label and marker from map/clusters + if (options.lat === this._marks[options.type][i].lat && options.lng === this._marks[options.type][i].lng) { + this._marks[options.type][i].name = options.name.value; + this._marks[options.type][i].description = options.description.value; + this._marks[options.type][i].rate = options.rating; + if (options.price) { + this._marks[options.type][i].price = options.price; + } + const popup = new MarkPopup(options, dom => { + options.dom = dom; + options.marker.setPopupContent(popup.dom); + options.popup = popup; + // Send data to server + this._kom[`${options.type}Edited`](options.id, Utils.formatMarker(options)).then(data => { + // Update marker data to session memory + options.id = data.id; + options.name = data.name; + options.description = data.description; + options.lat = data.lat; + options.lng = data.lng; + // Notify user through UI that marker has been successfully deleted + this.notification.raise(this.nls.notif(`${options.type}Edited`)); + }).catch(() => { + this.notification.raise(this.nls.notif(`${options.type}NotEdited`)); + }).finally(() => { + this._modal.close(null, true); + }); + }); + break; + } + } + } + + + // ======================================================================== // + // --------------------------- User profile ------------------------------- // + // ======================================================================== // + + + + /** + * @method + * @name userProfile + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The userProfile() method will request the user modal, which contains + * the user preferences, and the user profile information + *
        + **/ + userProfile() { + this._modal = ModalFactory.build('User'); + } + + + updateProfilePicture(options) { + this._modal.close(null, true); + setTimeout(() => { + this._modal = ModalFactory.build('UpdateProfilePicture', options); + }, 200); + } + + + _onProfilePictureUpdated(options) { + this._kom.patchImage(`api/user/${this._user.id}/profile-picture/`, { + profile_picture: document.getElementById('wip-pp').src, + minX: Math.round(options.imageResizer.getMinPoint().x), + minY: Math.round(options.imageResizer.getMinPoint().y), + maxX: Math.round(options.imageResizer.getMaxPoint().x), + maxY: Math.round(options.imageResizer.getMaxPoint().y) + }).then(() => { + this.notification.raise(this.nls.notif('uploadPPSuccess')); + }).catch(() => { + this.notification.raise(this.nls.notif('uploadPPFailed')); + }).finally(() => { + this._modal.close(null, true); + // Reload user from server with new path and close modal + this._initUser().then(() => { + setTimeout(() => { + this.userProfile(); + }, 200); + }); + }); + } + + + /** + * @method + * @name hidShowMenu + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The hidShowMenu() method will request the hide show modal, which all + * toggles for map elements ; spots/shops/bars + *
        + **/ + hidShowMenu() { + this._modal = ModalFactory.build('HideShow'); + } + + + // ======================================================================== // + // ---------------------------- Class accessors --------------------------- // + // ======================================================================== // + + + /** + * @public + * @property {Object} map + * Leaflet.js map getter + **/ + get map() { + return this._map; + } + + + /** + * @public + * @property {Object} marks + * Leaflet.js marks that holds spot/shop/bar marks as subkeys + **/ + get marks() { + return this._marks; + } + + + /** + * @public + * @property {Object} clusters + * Clusters that holds spot/shop/bar marks as subkeys + **/ + get clusters() { + return this._clusters; + } + + + /** + * @public + * @property {Object} user + * The session user object + **/ + get user() { + return this._user; + } + + + /** + * @public + * @property {Object} kom + * The server communication component + **/ + get kom() { + return this._kom; + } + + + /** + * @public + * @property {Object} notification + * The app notification handler + **/ + get notification() { + return this._notification; + } + + + /** + * @public + * @property {Object} nls + * The LangManager getter + **/ + get nls() { + return this._lang; + } + + +} + + +export default BeerCrackerz; diff --git a/front/src/BeerCrackerz.scss b/front/src/BeerCrackerz.scss new file mode 100644 index 0000000..a28c3d6 --- /dev/null +++ b/front/src/BeerCrackerz.scss @@ -0,0 +1,101 @@ +@import 'scss/utils/base'; + +main.beer-crakerz-map { + color: var(--color-txt); + height: 100%; + width: 100%; + z-index: 10; + + .new-poi { + text-align: center; + + p { + font-size: var(--font-size-l3); + font-weight: bold; + } + + button { + margin: var(--font-size) 0; + } + } +} + +nav { + display: flex; + bottom: 1rem; + position: absolute; + right: 1rem; + width: 48px; + z-index: 20; + + transition: right var(--aside-transtion-duration); + + img { + cursor: pointer; + border-radius: 50%; + box-shadow: 0px 0px 1rem var(--color-bg); +/*box-shadow: 0px 0px 1rem var(--color-bg-a5);*/ + width: 100%; + } + + .cmd-bar { + display: flex; + flex-direction: column; + width: 100%; + + .cmd-wrapper { + align-items: center; + background: var(--color-bg); + background-clip: padding-box; + border: 2px solid var(--color-bg-a1); + border-radius: .5rem; + box-shadow: 0 0 1rem var(--color-bg-a9); + display: flex; + justify-content: center; + margin: 2px 0; + padding: 2px; + width: 100%; + + &:first-child { + margin-top: 0; + } + + &:last-child { + margin-bottom: 0; + } + + img { + box-shadow: inherit; + height: 100%; + width: 100%; + transition: all .2s; + + &:active, + &:focus, + &:hover { + border: solid 1px var(--color-txt); + border-radius: .5rem; + padding: .2rem; + /* Blue filter */ + filter: invert(53%) sepia(30%) saturate(1977%) hue-rotate(155deg) brightness(88%) contrast(102%); + } + + &[class$="-on"] { + /* Filter for #ffa800 */ + filter: invert(63%) sepia(54%) saturate(1053%) hue-rotate(359deg) brightness(100%) contrast(106%); + } + } + + &.logo img { + padding: .1rem; + } + } + } +} + +@import 'scss/utils/overlay'; +@import 'scss/component/notification'; +@import 'scss/component/zoomslider'; +@import 'scss/component/imageresizer'; +@import 'scss/utils/leaflet'; +@import 'scss/utils/responsive'; diff --git a/front/src/BeerCrackerzAuth.js b/front/src/BeerCrackerzAuth.js new file mode 100644 index 0000000..acd6c55 --- /dev/null +++ b/front/src/BeerCrackerzAuth.js @@ -0,0 +1,864 @@ +import './BeerCrackerzAuth.scss'; +import Kom from './js/core/Kom.js'; +import LangManager from './js/core/LangManager.js'; + +import VisuHelper from './js/ui/VisuHelper.js'; +import MarkPopup from './js/ui/MarkPopup'; +import ZoomSlider from './js/ui/component/ZoomSlider.js'; + +import CustomEvents from './js/utils/CustomEvents.js'; +import Utils from './js/utils/Utils.js'; +import AccuracyEnum from './js/utils/enums/AccuracyEnum.js'; +import ClustersEnum from './js/utils/enums/ClusterEnum.js'; +import ProvidersEnum from './js/utils/enums/ProviderEnum.js'; +import MapEnum from './js/utils/enums/MapEnum.js'; +import MarkersEnum from './js/utils/enums/MarkerEnum.js'; + + +window.VERSION = '0.1.0'; +window.Evts = new CustomEvents(); + + +class BeerCrackerzAuth { + + + /** + * @summary The BeerCrackerzAuth main component + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * This component handles all the authentication pages for BeerCrackerz. It provides the login, the + * register and the forgot password process. It also provides a public map so unauthenticated user + * can still browse the best BeerCrackerz spots. For more information, please consult the application + * description page at https://about.beercrackerz.org/ + *
        + **/ + constructor() { + /** + * The minimal user object holds position and accuracy + * @type {Object} + * @private + **/ + this._user = { + lat: 48.853121540141096, // Default lat to Paris Notre-Dame latitude + lng: 2.3498955769881156, // Default lng to Paris Notre-Dame longitude + accuracy: 0 // Accuracy in meter given by geolocation API + }; + /** + * The stored marks for spots, shops and bars + * @type {Object} + * @private + **/ + this._marks = { + spot: [], + shop: [], + bar: [] + }; + /** + * The stored clusters for markers, see Leaflet.markercluster plugin + * @type {Object} + * @private + **/ + this._clusters = { + spot: {}, + shop: {}, + bar: {} + }; + /** + * The Aside DOM container + * @type {Object} + * @private + **/ + this._aside = null; + /** + * The Aside expand status + * @type {Boolean} + * @private + **/ + this._isAsideExpanded = true; + /** + * The server communication class + * @type {Object} + * @private + **/ + this._kom = null; + /** + * The frontend i18n manager + * @type {Object} + * @private + **/ + this._lang = new LangManager(); + + this._init(); + } + + + // ======================================================================== // + // -------------------------- App initialization -------------------------- // + // ======================================================================== // + + + /** + * @method + * @name _init + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _init() method handle the whole app initialization sequence. It first + * set the aside content to login (as it comes with the base welcome.html template), + * then initialize the communication and notification handler, and will finally + * initialize the whole map, markers and interactivity. + *
        + **/ + _init() { + // Force default language to english for first page loading + if (Utils.getPreference('selected-lang') === null) { + Utils.setPreference('selected-lang', 'en'); + } + this.nls.updateLang(Utils.getPreference('selected-lang')).then(() => { + // By default, the template contains the login aside, no need to fetch it + this._handleLoginAside(); + this._kom = new Kom(); + // We ensure the Kom layer is valid and ready to go any further + if (this._kom.isValid === true) { + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + if (params.activate) { + const error = document.getElementById('login-error'); + error.classList.add('visible'); + if (params.activate === 'True') { + error.classList.add('success'); + error.innerHTML = this.nls.register('activationSuccess'); + } else { + error.innerHTML = this.nls.register('activationError'); + } + } else if (params.uidb64 && params.token) { + this._loadForgotPasswordAside(params); + } + + this._initMap() + .then(this._initGeolocation.bind(this)) + .then(this._initMarkers.bind(this)) + .then(this._initEvents.bind(this)) + .catch(this._fatalError.bind(this)); + } else { + this._fatalError({ + file: 'Kom.js', + msg: (this._kom.csrf === null) ? `The CSRF token doesn't exists in cookies` : `The headers amount is invalid` + }); + } + }); + } + + + /** + * @method + * @async + * @name _initMap + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite), + * add scale control, remove zoom control and set map bounds. + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initMap() { + return new Promise(resolve => { + // Use main div to inject OSM into + this._map = window.L.map('beer-crakerz-map', { + zoomControl: false, + }).setView([48.853121540141096, 2.3498955769881156], 12); + // Add meter and feet scale on map + window.L.control.scale().addTo(this._map); + // Place user marker on the map + this._drawUserMarker(); + // Prevent panning outside of the world's edge + this._map.setMaxBounds(MapEnum.mapBounds); + // Add layer group to interface + const baseMaps = {}; + baseMaps[`

        ${this.nls.map('planLayerOSM')}

        `] = ProvidersEnum.planOsm; + baseMaps[`

        ${this.nls.map('satLayerEsri')}

        `] = ProvidersEnum.satEsri; + // Append layer depending on user preference + ProvidersEnum.planOsm.addTo(this._map); + // Add layer switch radio on bottom right of the map + window.L.control.layers(baseMaps, {}, { position: 'bottomright' }).addTo(this._map); + // Init zoom slider when map has been created + this._zoomSlider = new ZoomSlider(this._map); + resolve(); + }); + } + + + /** + * @method + * @async + * @name _initGeolocation + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _initGeolocation() method will request from browser the location authorization. + * Once granted, an event listener is set on any position update, so it can update the + * map state and the markers position. This method can be called again, only if the + * geolocation watch has been cleared ; for example when updating the accuracy options. + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initGeolocation() { + return new Promise(resolve => { + if ('geolocation' in navigator) { + this._watchId = navigator.geolocation.watchPosition(position => { + // Update saved user position + this._user.lat = position.coords.latitude; + this._user.lng = position.coords.longitude; + this._user.accuracy = position.coords.accuracy; + // Only draw marker if map is already created + if (this._map) { + this._drawUserMarker(); + } + }, null, AccuracyEnum.high); + resolve(); + } else { + resolve(); + } + }); + } + + + /** + * @method + * @async + * @name _initMarkers + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _initEvents() method will initialize all saved marker into the map. + * Markers must be retrieved from server with a specific format to ensure it works + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initMarkers() { + return new Promise(resolve => { + // Init map clusters for marks to be displayed (disable clustering at opened popup zoom level) + this._clusters.spot = ClustersEnum.spot; + this._clusters.shop = ClustersEnum.shop; + this._clusters.bar = ClustersEnum.bar; + + this._map.addLayer(this._clusters.spot); + this._map.addLayer(this._clusters.shop); + this._map.addLayer(this._clusters.bar); + + const iterateMarkers = mark => { + const popup = new MarkPopup(mark, dom => { + mark.dom = dom; + mark.marker = VisuHelper.addMark(mark); + mark.popup = popup; + this._marks[mark.type].push(mark); + this._clusters[mark.type].addLayer(mark.marker); + }); + }; + + this._kom.getSpots().then(spots => { + for (let i = 0; i < spots.length; ++i) { + iterateMarkers(spots[i]); + } + }); + + this._kom.getShops().then(shops => { + for (let i = 0; i < shops.length; ++i) { + iterateMarkers(shops[i]); + } + }); + + this._kom.getBars().then(bars => { + for (let i = 0; i < bars.length; ++i) { + iterateMarkers(bars[i]); + } + }); + + resolve(); + }); + } + + + /** + * @method + * @async + * @name _initEvents + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _initEvents() method will listen to all required events to manipulate the map. Those events + * are both for commands and for map events (click, drag, zoom and layer change). + *
        + * @returns {Promise} A Promise resolved when preferences are set + **/ + _initEvents() { + return new Promise(resolve => { + // Map is dragged by user mouse/finger + this._map.on('drag', () => { + // Constrain pan to the map bounds + this._map.panInsideBounds(MapEnum.mapBounds, { animate: true }); + }); + // Update map view for big popups + this._map.on('popupopen', event => { + const px = this._map.project(event.target._popup._latlng); + px.y -= event.target._popup._container.clientHeight / 2; + this._map.panTo(this._map.unproject(px), { animate: true }); + }); + // Center on command + document.getElementById('center-on').addEventListener('click', () => { + this._map.flyTo([this._user.lat, this._user.lng], 18); + }); + + window.Evts.subscribe('centerOn', VisuHelper.centerOn.bind(VisuHelper)); + resolve(); + }); + } + + + /** + * @method + * @name _fatalError + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _fatalError() method will handle all fatal errors from which the app + * can't recover. It redirects to the error page and send info through the referrer + * so the error page can properly displays it to the user + *
        + * @param {Object} err - The error object with its info + * @param {Number} [err.status] - The HTTP error code + * @param {String} [err.url] - The URL that generated the HTTP error + * @param {String} [err.file] - The file in which the fatal error happened + * @param {String} [err.msg] - The custom error message + **/ + _fatalError(err) { + if (window.DEBUG === false) { // In production, do the actual redirection + // We add params to referrer then redirect to error page so the information can be displayed + if (err && err.status) { // HTTP or related error + window.history.pushState('', '', `/welcome?&page=welcome&code=${err.status}&url=${err.url}&msg=${err.msg}`); + } else if (err && err.file && err.msg) { // File or process error + window.history.pushState('', '', `/welcome?&page=welcome&file=${err.file}&msg=${err.msg}`); + } else { // Generic error fallback + window.history.pushState('', '', `/welcome?&page=welcome&file=BeerCrackerzAuth.js&msg=An unknown error occured`); + } + // Now redirect the user to error page + window.location.href = '/error'; + } else { + console.error(err); + } + } + + + // ======================================================================== // + // -------------------------- Aside interactivity ------------------------- // + // ======================================================================== // + + + /** + * @method + * @name _toggleAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _toggleAside() method will expand or collapse the aside, depending on the + * `this._isAsideExpanded` flag state. To be used as a callba, adding useful parameters to url before redirectck on aside expander. + *
        + **/ + _toggleAside() { + if (this._isAsideExpanded === true) { // Collapsing aside + this._isAsideExpanded = false; + document.documentElement.style.setProperty('--aside-offset', '-40rem'); + document.getElementById('aside-expander-icon').src = '/static/img/logo/left.svg'; + document.getElementById('expander-back').classList.add('visible'); + setTimeout(() => document.getElementById('aside-expander').style.left = '-6rem', 300); + } else { // Expanding aside + this._isAsideExpanded = true; + document.documentElement.style.setProperty('--aside-offset', '0rem'); + document.getElementById('aside-expander-icon').src = '/static/img/logo/right.svg'; + document.getElementById('aside-expander').style.left = '0'; + document.getElementById('expander-back').classList.remove('visible'); + } + } + + + /** + * @method + * @async + * @name _loadAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _loadAside() method is a generic method to load an HTML template and replace + * the aside DOM content with that template, aswell as updating the document's class. + *
        + * @param {String} type - The aside to load in login/register/forgot-password + * @returns {Promise} A Promise resolved when template is loaded and in DOM + **/ + _loadAside(type) { + return new Promise((resolve, reject) => { + this._kom.getTemplate(`/aside/${type}`).then(dom => { + //document.body.className = 'login dark-theme'; // Clear previous css class + document.body.classList.add(type); // Update body class with current aside view + // We need to get aside at the last moment because of nls that changed HTML content + this._aside = document.getElementById('aside'); + this._aside.innerHTML = ''; // Clear HTML content + this._aside.appendChild(dom); // Replace with current aside dom + resolve(); + }).catch(reject); + }); + } + + + /** + * @method + * @name _loadLoginAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _loadLoginAside() method will load the login content into the aside + *
        + **/ + _loadLoginAside(checkMail = false) { + this._loadAside('login').then(this._handleLoginAside.bind(this, checkMail)).catch(err => { + err.msg = `Couldn't fetch or build the login aside`; + this._fatalError(err); + }); + } + + + /** + * @method + * @name _loadRegisterAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _loadRegisterAside() method will load the register content into the aside + *
        + **/ + _loadRegisterAside() { + this._loadAside('register').then(this._handleRegisterAside.bind(this)).catch(err => { + err.msg = `Couldn't fetch or build the register aside`; + this._fatalError(err); + }); + } + + + /** + * @method + * @name _loadForgotPasswordAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _loadForgotPasswordAside() method will load the forgot password content into the aside + *
        + **/ + _loadForgotPasswordAside(params) { + if (params.uidb64 && params.token) { + this._loadAside('resetpassword').then(this._handleResetPasswordAside.bind(this, params)).catch(err => { + err.msg = `Couldn't fetch or build the forgot password aside`; + this._fatalError(err); + }); + } else { + this._loadAside('forgotpassword').then(this._handleForgotPasswordAside.bind(this)).catch(err => { + err.msg = `Couldn't fetch or build the forgot password aside`; + this._fatalError(err); + }); + } + } + + + /** + * @method + * @name _handleLoginAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _handleLoginAside() method will replace the aside content with the login template, + * then it will handle its i18n, and all of its interactivity to submit login form to the server. + *
        + **/ + _handleLoginAside(checkMail = false) { + // Update page nls according to browser language + document.title = this.nls.login('headTitle'); + this.nls.handleLoginAside(document.getElementById('aside')); + + const error = document.getElementById('login-error'); + const username = document.getElementById('username'); + const password = document.getElementById('password'); + + if (checkMail === true) { + error.classList.add('visible'); + error.innerHTML = this.nls.login('checkMail'); + } + + // useful login method for field check and server response check + const _frontFieldValidation = () => { + error.className = 'error'; + // Handling empty error cases + if (username.value === '' && password.value === '') { + error.classList.add('visible'); + error.innerHTML = this.nls.login('bothEmpty'); + username.classList.add('error'); + password.classList.add('error'); + return false; + } else if (username.value === '') { + error.classList.add('visible'); + error.innerHTML = this.nls.login('usernameEmpty'); + username.classList.add('error'); + return false; + } else if (password.value === '') { + error.classList.add('visible'); + error.innerHTML = this.nls.login('passwordEmpty'); + password.classList.add('error'); + return false; + } + return true; + }; + const _backValidation = (e) => { + // Check response and handle status codes + // If all front and back tests are ok, redirect to auth + // If the user manually force redirection to authindex, + // the server should reject the request as the user is not authenticated + window.location = '/'; + }; + const _submit = () => { + // Reset error css classes + error.classList.remove('visible'); + username.classList.remove('error'); + password.classList.remove('error'); + if (_frontFieldValidation()) { + this._kom.post('/api/auth/login/', { + username: username.value, + password: password.value + }).then(_backValidation).catch(e => { + error.classList.add('visible'); + if (e.status === 401) { + error.innerHTML = this.nls.login('credsInvalid'); + } else { + error.innerHTML = this.nls.login('serverError'); + } + }); + } + }; + // Submit click event + document.getElementById('login-submit').addEventListener('click', _submit.bind(this), false); + password.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } }); + // Register event + document.getElementById('register-aside').addEventListener('click', this._loadRegisterAside.bind(this), false); + document.getElementById('forgot-password').addEventListener('click', this._loadForgotPasswordAside.bind(this), false); + document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false); + } + + + /** + * @method + * @name _handleRegisterAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _handleRegisterAside() method will replace the aside content with the register template, + * then it will handle its i18n, and all of its interactivity to submit register form to the server. + *
        + **/ + _handleRegisterAside() { + // Update page nls according to browser language + const aside = document.getElementById('aside'); + document.title = this.nls.register('headTitle'); + this.nls.handleRegisterAside(aside); + const error = document.getElementById('register-error'); + const username = document.getElementById('username'); + const mail = document.getElementById('mail'); + const password1 = document.getElementById('password1'); + const password2 = document.getElementById('password2'); + // useful login method for field check and server response check + const _frontFieldValidation = () => { + // Handling empty error cases + if (username.value === '' || mail.value === '' || password1.value === '' || password2.value === '') { + error.classList.add('visible'); + error.innerHTML = this.nls.register('fieldEmpty'); + if (username.value === '') { username.classList.add('error'); } + if (mail.value === '') { mail.classList.add('error'); } + if (password1.value === '') { password1.classList.add('error'); } + if (password2.value === '') { password2.classList.add('error'); } + return false; + } else if (password1.value !== password2.value) { + error.classList.add('visible'); + error.innerHTML = this.nls.register('notMatchingPassword'); + password1.classList.add('error'); + password2.classList.add('error'); + return false; + } + return true; + }; + const _backValidation = () => { + // Redirect aside to login + this._loadLoginAside(true); + }; + const _submit = () => { + // Reset error css classes + error.classList.remove('visible'); + username.classList.remove('error'); + mail.classList.remove('error'); + password1.classList.remove('error'); + password2.classList.remove('error'); + if (_frontFieldValidation()) { + this._kom.post('/api/auth/register/', { + username: username.value, + email: mail.value, + password1: password1.value, + password2: password2.value + }).then(_backValidation).catch(() => { + error.classList.add('visible'); + error.innerHTML = this.nls.register('serverError'); + }); + } + }; + // Submit click event + document.getElementById('register-submit').addEventListener('click', _submit.bind(this), false); + password2.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } }); + // Register event + document.getElementById('login-aside').addEventListener('click', this._loadLoginAside.bind(this), false); + document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false); + } + + + /** + * @method + * @name _handleForgotPasswordAside + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _handleForgotPasswordAside() method will replace the aside content with the fogot password + * template, then it will handle its i18n, and all of its interactivity to submit forgot password + * form to the server. + *
        + **/ + _handleForgotPasswordAside() { + // Update page nls according to browser language + const aside = document.getElementById('aside'); + document.title = this.nls.forgotPassword('headTitle'); + this.nls.handleForgotPasswordAside(aside); + const error = document.getElementById('forgot-password-error'); + const mail = document.getElementById('mail'); + // useful login method for field check and server response check + const _frontFieldValidation = () => { + // Handling empty error cases + if (mail.value === '') { + error.classList.add('visible'); + error.innerHTML = this.nls.forgotPassword('fieldEmpty'); + if (mail.value === '') { + mail.classList.add('error'); + } + return false; + } + return true; + }; + const _backValidation = () => { + // Check response and handle status codes + error.classList.add('visible'); + error.innerHTML = this.nls.login('checkMail'); + }; + const _submit = () => { + // Reset error css classes + error.classList.remove('visible'); + mail.classList.remove('error'); + if (_frontFieldValidation()) { + this._kom.post('/api/auth/password-reset-request/', { + email: mail.value + }, null).then(_backValidation).catch(() => { + error.classList.add('visible'); + error.innerHTML = this.nls.forgotPassword('serverError'); + }); + } + }; + // Submit click event + document.getElementById('forgot-password-submit').addEventListener('click', _submit.bind(this), false); + mail.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } }); + + document.getElementById('login-aside').addEventListener('click', this._loadLoginAside.bind(this), false); + document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false); + } + + + _handleResetPasswordAside(params) { + // Update page nls according to browser language + const aside = document.getElementById('aside'); + document.title = this.nls.resetPassword('headTitle'); + this.nls.handleResetPasswordAside(aside); + const error = document.getElementById('reset-password-error'); + const password1 = document.getElementById('password1'); + const password2 = document.getElementById('password2'); + // useful login method for field check and server response check + const _frontFieldValidation = () => { + // Handling empty error cases + if (password1.value === '' || password2.value === '') { + error.classList.add('visible'); + error.innerHTML = this.nls.resetPassword('fieldEmpty'); + if (password1.value === '') { password1.classList.add('error'); } + if (password2.value === '') { password2.classList.add('error'); } + return false; + } else if (password1.value !== password2.value) { + error.classList.add('visible'); + error.innerHTML = this.nls.resetPassword('notMatchingPassword'); + password1.classList.add('error'); + password2.classList.add('error'); + return false; + } + return true; + }; + const _backValidation = () => { + // Redirect aside to login + this._loadLoginAside(); + }; + const _submit = () => { + // Reset error css classes + error.classList.remove('visible'); + password1.classList.remove('error'); + password2.classList.remove('error'); + if (_frontFieldValidation()) { + this._kom.post(`/api/auth/password-reset/?uidb64=${params.uidb64}&token=${params.token}`, { + password1: password1.value, + password2: password2.value + }, null).then(_backValidation).catch(() => { + error.classList.add('visible'); + error.innerHTML = this.nls.resetPassword('serverError'); + }); + } + }; + // Submit click event + document.getElementById('reset-password-submit').addEventListener('click', _submit.bind(this), false); + password2.addEventListener('keydown', e => { if (e.key === 'Enter') { _submit(); } }); + + document.getElementById('login-aside').addEventListener('click', this._loadLoginAside.bind(this), false); + document.getElementById('aside-expander').addEventListener('click', this._toggleAside.bind(this), false); + } + + + // ======================================================================== // + // -------------------------- Public map methods -------------------------- // + // ======================================================================== // + + + /** + * @method + * @name _drawUserMarker + * @private + * @memberof BeerCrackerzAuth + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * The _drawUserMarker() method will draw the user marker to the position received + * from the geolocation API. If the marker doesn't exist yet, it will create it and + * place it to its default position (see constructor/this._user). + *
        + **/ + _drawUserMarker() { + if (!this.user.marker) { // Create user marker if not existing + this.user.type = 'user'; + this.user.marker = VisuHelper.addMark(this.user); + } else { // Update user marker position, range, and accuracy circle + this.user.marker.setLatLng(this.user); + } + } + + + // ======================================================================== // + // ---------------------------- Class accessors --------------------------- // + // ======================================================================== // + + + get kom() { + return this._kom; + } + + + /** + * @public + * @property {Object} map + * Leaflet.js map getter + **/ + get map() { + return this._map; + } + + + /** + * @public + * @property {Object} marks + * Leaflet.js marks that holds spot/shop/bar marks as subkeys + **/ + get marks() { + return this._marks; + } + + + /** + * @public + * @property {Object} user + * The session user object + **/ + get user() { + return this._user; + } + + + /** + * @public + * @property {Object} nls + * The LangManager getter + **/ + get nls() { + return this._lang; + } + + +} + + +export default BeerCrackerzAuth; diff --git a/front/src/BeerCrackerzAuth.scss b/front/src/BeerCrackerzAuth.scss new file mode 100644 index 0000000..8819d12 --- /dev/null +++ b/front/src/BeerCrackerzAuth.scss @@ -0,0 +1,186 @@ +@import 'scss/utils/base'; + +/* ---------------------------------------------------------- * + * The BeerCrackerzAuth style bundle * + * Elements have given z-index : * + * 10 : Main map, that occupies the whole page * + * 20 : The page header, only displayed when aside is collapsed * + * 30 : The Aside that must be over everything at anytime * + * ---------------------------------------------------------- */ + +:root { + --aside-width: 40rem; + --aside-offset: 0rem; + --aside-transtion-duration: var(--transition-x2); +} + +nav { + display: flex; + bottom: 1rem; + position: absolute; + height: 48px; + /* Force nav to stick to aside's left even when collapsed */ + right: calc(var(--aside-width) + var(--aside-offset) + 1rem); + width: 48px; + z-index: 20; + + transition: right var(--aside-transtion-duration); + + img { + cursor: pointer; + border-radius: 50%; + box-shadow: 0px 0px 1rem var(--color-bg); + width: 100%; + } + + .cmd-bar { + display: flex; + width: 100%; + + .cmd-wrapper { + align-items: center; + background: var(--color-bg); + background-clip: padding-box; + border: 2px solid var(--color-bg-a1); + border-radius: .5rem; + box-shadow: 0 0 1rem var(--color-bg-a9); + display: flex; + justify-content: center; + padding: .2rem; + width: 100%; + + img { + box-shadow: inherit; + transition: all var(--transition); + + &:active, + &:focus, + &:hover { + border: solid 1px rgb(185, 185, 185); + border-radius: .5rem; + padding: .2rem; + + filter: invert(63%) sepia(54%) saturate(1053%) hue-rotate(359deg) brightness(100%) contrast(106%); + } + } + } + } +} + +.expander-back { + background-color: var(--color-bg-a9); + border: 2px solid var(--color-bg); + border-radius: .5rem; + height: 48px; + opacity: 0; + position: absolute; + right: 1rem; + top: 1rem; + width: 48px; + z-index: 20; + + transition: opacity var(--transition); + + &.visible { + opacity: 1; + } +} + +main.beer-crakerz-map { + height: 100%; + width: 100%; + z-index: 10; + + transition: width var(--aside-transtion-duration); + + .leaflet-control-layers.leaflet-control { + right: calc(var(--aside-width) + var(--aside-offset) + 5.8rem); + transition: right var(--aside-transtion-duration); + } +} + +aside { + background-color: var(--color-bg-a9); + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-between; + max-width: var(--aside-width); + position: absolute; + right: var(--aside-offset); + text-align: center; + top: 0; + width: 100%; + z-index: 30; + + transition: all var(--aside-transtion-duration); + + .aside-expander { + cursor: pointer; + height: 48px; + left: 0; + margin: 0; + position: absolute; + top: 1rem; + filter: drop-shadow(0 0 1px var(--color-bg)); + width: 48px; + + transition: left var(--transition); + } + + h1 { + margin-bottom: 0; + } + + header { + margin-top: 5rem; + } + + main { + margin: 0 4rem; + text-align: left; + + button { + margin: 2.9rem auto var(--font-size); + } + + p { + margin-bottom: .5rem; + text-align: right; + + &[class*="error"] { + color: transparent; + font-style: italic; + font-weight: bold; + text-align: center; + + transition: color var(--transition); + + &.visible { + color: var(--color-primary); + } + + &.success { + color: #a1ff86; + } + } + } + } + + footer { + margin-bottom: 5rem; + } +} + +@import 'scss/utils/overlay'; +@import 'scss/component/zoomslider'; +@import 'scss/utils/leaflet'; + +.leaflet-control-layers.leaflet-control { + bottom: 0; +} + +.zoom-slider.opened { + /* Force slider to stick to aside's left even when collapsed */ + right: calc(var(--aside-width) + var(--aside-offset) + 1rem); +} diff --git a/front/src/js/core/Kom.js b/front/src/js/core/Kom.js new file mode 100644 index 0000000..2357e40 --- /dev/null +++ b/front/src/js/core/Kom.js @@ -0,0 +1,569 @@ +class Kom { + + + /** + * @summary Server communication abstraction + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * This class is the main object to deal with when requesting something from the server. + * It handle all urls calls (GET, POST), treat responses or handle errors using + * Promise.
        Because it uses Promise, success and errors are to be handled in the caller + * function, using .then() and .catch(). To properly deal with POST request, + * the session must contain a csrf token in cookies. Otherwise, those POST call may fail. + *
        + **/ + constructor() { + /** + * User session CSRF token to use in POST request + * @type {string} + * @private + **/ + this._csrfToken = this._getCsrfCookie(); + /** + * Array of HTTP headers to be used in HTTP calls + * @type {Array[]} + * @private + **/ + this._headers = this._createRequestHeaders(); + /** + * Wether the Kom class headers are properly built + * @type {Boolean} + * @public + **/ + this.isValid = this._checkValidity(); // Check that CSRF token exists and that headers are properly created + } + + + // ======================================================================== // + // ------------------------- Class initialization ------------------------- // + // ======================================================================== // + + + /** + * @method + * @name _getCsrfCookie + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Extract CSRF token value from client cookies and returns it as a string. Returns an empty + * string by default. This method is required to be called on construction. + *
        + * @return {String} - The CSRF token string + **/ + _getCsrfCookie() { + if (document.cookie && document.cookie !== '') { + const cookies = document.cookie.split(';'); + for (let i = 0; i < cookies.length; ++i) { + // Parse current cookie to extract its properties + const cookie = cookies[i].split('='); + if (cookie !== undefined && cookie[0].toLowerCase().includes('srf')) { + // Found a matching cookie for csrftoken value, return as decoded string + return decodeURIComponent(cookie[1]); + } + } + } + // Return empty string by default, POST calls may fail + return null; + } + + + /** + * @method + * @name _createRequestHeaders + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Fills Kom _headers private member array, to use in HTTP requests later on. + * This method is required to be called on construction. + *
        + * @return {Array[]} - The headers array, length 3, to be used in HTTP requests + **/ + _createRequestHeaders() { + return [ + ['Content-Type', 'application/json; charset=UTF-8'], + ['Accept', 'application/json'], + ['X-CSRFToken', this._csrfToken] + ]; + } + + + /** + * @method + * @name _checkValidity + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Check the Kom instance validity (eough headers) to ensure its properties validity. + *
        + **/ + _checkValidity() { + if (this._csrfToken !== null) { + if (this._headers.length !== 3) { + return false; + } + } else { + return false; + } + + return true; + } + + + // ======================================================================== // + // ------------------------- Response formatting -------------------------- // + // ======================================================================== // + + + /** + * @method + * @async + * @name _resolveAs + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Generic tool method used by private methods on fetch responses to format output in the provided + * format. It must be either `json`, `text`, `raw` or `dom`. + *
        + * @param {String} type - The type of resolution, can be `json`, `text`, `raw` or `dom` + * @param {Object} response - The fetch response object + * @returns {Promise} The request Promise, format response as an object on resolve, as error code string on reject + **/ + _resolveAs(type, response) { + return new Promise((resolve, reject) => { + if (response) { + if (type === 'raw') { // Raw are made in XMLHttpRequest and need special handling + if (response.status === 200) { + resolve(response.responseText); + } else { + reject(response.status); + } + } else if (type === 'json' || type === 'text') { // Call are made using fetch API + if (response[type]) { + resolve(response[type]()); + } else { // Fallback on standard error handling + reject(response.status); + } + } else if (type === 'dom') { + response.text().then(html => { + resolve(document.createRange().createContextualFragment(html)); + }).catch(reject); + } else { // Resolution type doesn't exists, resolving empty + resolve(); + } + } else { + reject('F_KOM_MISSING_ARGUMENT'); + } + }); + } + + + /** + * @method + * @async + * @name _resolveAsJSON + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Tool method used by public methods on fetch responses to format output data as JSON to be + * read in JavaScript code as objects. + *
        + * @param {Object} response - The fetch response object + * @returns {Promise} The request Promise, format response as an object on resolve, as error code string on reject + **/ + _resolveAsJSON(response) { + return this._resolveAs('json', response); + } + + + /** + * @method + * @async + * @name _resolveAsText + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Tool method used by public methods on fetch responses to format output data as text to be + * read in JavaScript code as string (mostly to parse HTML templates). + *
        + * @param {Object} response - The fetch response object + * @returns {Promise} The request Promise, format response as a string on resolve, as error code string on reject + **/ + _resolveAsText(response) { + return this._resolveAs('text', response); + } + + + /** + * @method + * @async + * @name _resolveAsDom + * @private + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * Tool method used by public methods on fetch responses to format output data as DOM fragment to be + * read in JavaScript code as HTML template. + *
        + * @param {Object} response - The fetch response object + * @returns {Promise} The request Promise, format response as a string on resolve, as error code string on reject + **/ + _resolveAsDom(response) { + return this._resolveAs('dom', response); + } + + + + // ======================================================================== // + // --------------------------- GET server calls --------------------------- // + // ======================================================================== // + + + /** + * @method + * @async + * @name get + * @public + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * GET HTTP request using the fetch API.
        resolve returns the + * response as an Object.
        reject returns an error key as a String. + * It is meant to perform API call to access database through the user interface. + *
        + * @param {String} url - The GET url to fetch data from, in supported back URLs + * @returns {Promise} The request Promise + **/ + get(url, resolution = this._resolveAsJSON.bind(this)) { + return new Promise((resolve, reject) => { + const options = { + method: 'GET', + headers: new Headers([this._headers[0]]) // Content type to JSON + }; + + fetch(url, options) + .then(data => { + // In case the request wen well but didn't gave the expected 200 status + if (data.status !== 200) { + reject(data); + } + return resolution(data); + }) + .then(resolve) + .catch(reject); + }); + } + + + /** + * @method + * @async + * @name getText + * @public + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * GET HTTP request using the fetch API.
        resolve returns the + * response as a String.
        reject returns an error key as a String. It is + * meant to perform API call to get HTML templates as string to be parsed as documents/documents fragments. + *
        + * @param {String} url - The GET url to fetch data from, in supported back URLs + * @returns {Promise} The request Promise + **/ + getText(url) { + return this.get(url, this._resolveAsText.bind(this)); + } + + + /** + * @method + * @async + * @name getText + * @public + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * GET HTTP request using the fetch API.
        resolve returns the + * response as a String.
        reject returns an error key as a String. It is + * meant to perform API call to get HTML templates as string to be parsed as documents/documents fragments. + *
        + * @param {String} url - The GET url to fetch data from, in supported back URLs + * @returns {Promise} The request Promise + **/ + getTemplate(url) { + return this.get(url, this._resolveAsDom.bind(this)); + } + + + // ======================================================================== // + // -------------------------- POST server calls --------------------------- // + // ======================================================================== // + + + /** + * @method + * @async + * @name post + * @public + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * POST HTTP request using the fetch API.
        Beware that the given options + * object match the url expectations.
        resolve + * returns the response as an Object.
        reject returns an error key as a String. + *
        + * @param {String} url - The POST url to fetch data from + * @param {Object} data - The JSON object that contains POST parameters + * @returns {Promise} The request Promise + **/ + post(url, data, resolution = this._resolveAsJSON.bind(this)) { + return new Promise((resolve, reject) => { + const options = { + method: 'POST', + headers: new Headers(this._headers), // POST needs all previously defined headers + body: JSON.stringify(data) + }; + + fetch(url, options) + .then(data => { + // In case the request wen well but didn't gave the expected 200 status + if (data.status >= 400) { + reject(data); + } + + if (resolution !== undefined && resolution !== null) { + return resolution(data); + } + + return data; + }) + .then(resolve) + .catch(reject); + }); + } + + + /** + * @method + * @async + * @name postText + * @public + * @memberof Kom + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * POST HTTP request using the fetch API.
        Beware that the given options + * object match the url expectations.
        resolve + * returns the response as a String.
        reject returns an error key as a String. + *
        + * @param {String} url - The POST url to fetch data from + * @param {Object} data - The JSON object that contains POST parameters + * @returns {Promise} The request Promise + **/ + postText(url, data) { + return this.post(url, data, this._resolveAsText.bind(this)); + } + + + postImage(url, data) { + return this.post(url, data, null); + } + + + // ======================================================================== // + // ------------------------- PATCH server calls --------------------------- // + // ======================================================================== // + + + patch(url, data, resolution = this._resolveAsJSON.bind(this)) { + return new Promise((resolve, reject) => { + const options = { + method: 'PATCH', + headers: new Headers(this._headers), // PATCH needs all previously defined headers + body: JSON.stringify(data) + }; + + fetch(url, options) + .then(data => { + // In case the request wen well but didn't gave the expected 200 status + if (data.status >= 400) { + reject(data); + } + + if (resolution !== undefined && resolution !== null) { + return resolution(data); + } + + return data; + }) + .then(resolve) + .catch(reject); + }); + } + + + patchImage(url, data) { + return this.patch(url, data, null); + } + + + // ======================================================================== // + // ------------------------- DELETE server calls -------------------------- // + // ======================================================================== // + + + delete(url, data, resolution = this._resolveAsJSON.bind(this)) { + return new Promise((resolve, reject) => { + const options = { + method: 'DELETE', + headers: new Headers(this._headers), // DELETE needs all previously defined headers + body: JSON.stringify(data) + }; + + fetch(url, options) + .then(data => { + // In case the request wen well but didn't gave the expected 200 status + if (data.status >= 400) { + reject(data); + } + + if (resolution !== undefined && resolution !== null) { + return resolution(data); + } + + return data; + }) + .then(resolve) + .catch(reject); + }); + } + + + // ======================================================================== // + // ------------------ BeerCrackerz server call shortcuts ------------------ // + // ======================================================================== // + + + _getMarks(type) { + return new Promise((resolve, reject) => { + this.get(`/api/${type}`).then(resolve).catch(reject); + }); + } + + + getSpots() { + return this._getMarks('spot'); + } + + + getShops() { + return this._getMarks('shop'); + } + + + getBars() { + return this._getMarks('bar'); + } + + + _saveMark(type, data) { + return this.post(`/api/${type}/`, data, this._resolveAsJSON.bind(this)); + } + + + spotCreated(data) { + return this._saveMark('spot', data); + } + + + shopCreated(data) { + return this._saveMark('shop', data); + } + + + barCreated(data) { + return this._saveMark('bar', data); + } + + + _editMark(type, id, data) { + if (!type || !id || !data) { Promise.reject(); } + return this.patch(`/api/${type}/${id}/`, data, this._resolveAsJSON.bind(this)); + } + + + spotEdited(id, data) { + return this._editMark('spot', id, data); + } + + + shopEdited(id, data) { + return this._editMark('shop', id, data); + } + + + barEdited(id, data) { + return this._editMark('bar', id, data); + } + + + _deleteMark(type, id, data) { + if (!type || !id || !data) { Promise.reject(); } + return this.delete(`/api/${type}/${id}/`, data, null); + } + + + spotDeleted(id, data) { + return this._deleteMark('spot', id, data); + } + + + shopDeleted(id, data) { + return this._deleteMark('shop', id, data); + } + + + barDeleted(id, data) { + return this._deleteMark('bar', id, data); + } + + + get csrf() { + return null; + } + + +} + + +export default Kom; diff --git a/front/src/js/core/LangManager.js b/front/src/js/core/LangManager.js new file mode 100644 index 0000000..fed6a7b --- /dev/null +++ b/front/src/js/core/LangManager.js @@ -0,0 +1,279 @@ +import Utils from '../utils/Utils.js'; +import SupportedLangEnum from '../utils/enums/SupportedLangEnum.js'; + + +class LangManager { + + + /** + * @summary Handle i18n for BeerCrackerz + * @author Arthur Beaulieu + * @since September 2022 + * @description + *
        + * This class will fetch and store all i18n keys for a given language to be used in BeerCrackerz. + *
        + * @param {String} lang - The selected language to fetch + * @param {Function} cb - The callback to call once i18n keys are loaded + * @param {Function} err - The callback to call if anything went wrong + **/ + constructor() { + this._lang = ''; + this._fullLang = ''; + this._values = {}; + } + + + _init() { + return new Promise((resolve, reject) => { + fetch(`/static/nls/${this._lang}.json`).then(data => { + // In case the request wen well but didn't gave the expected 200 status + if (data.status !== 200) { + data.msg = `Fetching the i18n file failed`; + reject(data); + } + + data.text().then(nls => { + try { + this._values = JSON.parse(nls); + } catch (err) { + data.msg = `Parsing the i18n file failed`; + reject(data); + } + + resolve(); + }).catch(reject); + }).catch(reject); + }); + } + + + updateLang(lang) { + return new Promise((resolve, reject) => { + if (this._lang !== lang) { + this._lang = (SupportedLangEnum.indexOf(lang) !== -1) ? lang : 'en'; + this._fullLang = lang; + this._init().then(resolve).catch(reject); + } else { + resolve(); + } + }); + } + + + debug(key) { + return this._values.debug[key] || ''; + } + + + notif(key) { + return this._values.notif[key] || ''; + } + + + nav(key) { + return this._values.nav[key] || ''; + } + + + map(key) { + return this._values.map[key] || ''; + } + + + spot(key) { + return this._values.spot[key] || ''; + } + + + shop(key) { + return this._values.shop[key] || ''; + } + + + bar(key) { + return this._values.bar[key] || ''; + } + + + popup(key) { + return this._values.popup[key] || ''; + } + + + modal(key) { + return this._values.modal[key] || ''; + } + + + login(key) { + return this._values.auth.login[key] || ''; + } + + + register(key) { + return this._values.auth.register[key] || ''; + } + + + forgotPassword(key) { + return this._values.auth.forgotPassword[key] || ''; + } + + + resetPassword(key) { + return this._values.auth.resetPassword[key] || ''; + } + + + // Auth page shortcut to update UI chunks of text in views + + + handleLoginAside(aside) { + Utils.replaceString(aside, '{LOGIN_SUBTITLE}', this.login('subtitle')); + Utils.replaceString(aside, '{LOGIN_HIDDEN_ERROR}', this.login('hiddenError')); + Utils.replaceString(aside, '{LOGIN_USERNAME_LABEL}', this.login('username')); + Utils.replaceString(aside, '{LOGIN_USERNAME_PASSWORD}', this.login('password')); + Utils.replaceString(aside, '{LOGIN_BUTTON}', this.login('login')); + Utils.replaceString(aside, '{LOGIN_NOT_REGISTERED}', this.login('notRegistered')); + Utils.replaceString(aside, '{LOGIN_REGISTER}', this.login('register')); + Utils.replaceString(aside, '{LOGIN_FORGOT_PASSWORD}', this.login('forgot')); + Utils.replaceString(aside, '{LOGIN_PASSWORD_RESET}', this.login('reset')); + } + + + handleRegisterAside(aside) { + Utils.replaceString(aside, '{REGISTER_SUBTITLE}', this.register('subtitle')); + Utils.replaceString(aside, '{REGISTER_HIDDEN_ERROR}', this.register('hiddenError')); + Utils.replaceString(aside, '{REGISTER_USERNAME_LABEL}', this.register('username')); + Utils.replaceString(aside, '{REGISTER_MAIL_LABEL}', this.register('mail')); + Utils.replaceString(aside, '{REGISTER_USERNAME_PASSWORD_1}', this.register('password1')); + Utils.replaceString(aside, '{REGISTER_USERNAME_PASSWORD_2}', this.register('password2')); + Utils.replaceString(aside, '{REGISTER_BUTTON}', this.register('register')); + Utils.replaceString(aside, '{REGISTER_ALREADY_DONE}', this.register('notRegistered')); + Utils.replaceString(aside, '{REGISTER_LOGIN}', this.register('login')); + } + + + handleForgotPasswordAside(aside) { + Utils.replaceString(aside, '{FORGOT_PASSWORD_SUBTITLE}', this.forgotPassword('subtitle')); + Utils.replaceString(aside, '{FORGOT_PASSWORD_ERROR}', this.register('hiddenError')); + Utils.replaceString(aside, '{FORGOT_PASSWORD_MAIL_LABEL}', this.forgotPassword('mail')); + Utils.replaceString(aside, '{FORGOT_PASSWORD_BUTTON}', this.forgotPassword('submit')); + Utils.replaceString(aside, '{FORGOT_PASSWORD_LOGIN_LABEL}', this.forgotPassword('loginLabel')); + Utils.replaceString(aside, '{FORGOT_PASSWORD_LOGIN}', this.forgotPassword('login')); + } + + + handleResetPasswordAside(aside) { + Utils.replaceString(aside, '{RESET_PASSWORD_SUBTITLE}', this.resetPassword('subtitle')); + Utils.replaceString(aside, '{RESET_PASSWORD_HIDDEN_ERROR}', this.resetPassword('hiddenError')); + Utils.replaceString(aside, '{RESET_PASSWORD_1}', this.resetPassword('password1')); + Utils.replaceString(aside, '{RESET_PASSWORD_2}', this.resetPassword('password2')); + Utils.replaceString(aside, '{RESET_PASSWORD_BUTTON}', this.resetPassword('reset')); + Utils.replaceString(aside, '{RESET_PASSWORD_LOGIN_LABEL}', this.resetPassword('loginLabel')); + Utils.replaceString(aside, '{RESET_PASSWORD_LOGIN}', this.resetPassword('login')); + } + + + // Main App shortcut to update UI chunks of text in views + + + addMarkModal(dom, type, action) { + Utils.replaceString(dom.querySelector(`#nls-${type}-title`), `{${type.toUpperCase()}_TITLE}`, this[type](`${action}Title`)); + Utils.replaceString(dom.querySelector(`#nls-${type}-subtitle`), `{${type.toUpperCase()}_SUBTITLE}`, this[type]('subtitle')); + Utils.replaceString(dom.querySelector(`#nls-${type}-name`), `{${type.toUpperCase()}_NAME}`, this[type]('nameLabel')); + Utils.replaceString(dom.querySelector(`#nls-${type}-desc`), `{${type.toUpperCase()}_DESC}`, this[type]('descLabel')); + Utils.replaceString(dom.querySelector(`#nls-${type}-rate`), `{${type.toUpperCase()}_RATE}`, this[type]('rateLabel')); + Utils.replaceString(dom.querySelector(`#nls-${type}-type`), `{${type.toUpperCase()}_TYPE}`, this[type]('typeLabel')); + Utils.replaceString(dom.querySelector(`#nls-${type}-modifiers`), `{${type.toUpperCase()}_MODIFIERS}`, this[type]('modifiersLabel')); + Utils.replaceString(dom.querySelector(`#${type}-submit`), `{${type.toUpperCase()}_SUBMIT}`, this.nav(action)); + Utils.replaceString(dom.querySelector(`#${type}-close`), `{${type.toUpperCase()}_CANCEL}`, this.nav('cancel')); + if (dom.querySelector(`#nls-${type}-price`)) { + Utils.replaceString(dom.querySelector(`#nls-${type}-price`), `{${type.toUpperCase()}_PRICE}`, this[type]('priceLabel')); + } + } + + + deleteMarkModal(dom) { + Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('deleteMarkTitle')); + Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{MODAL_DESC}`, this.modal('deleteMarkDesc')); + Utils.replaceString(dom.querySelector(`#cancel-close`), `{MODAL_CANCEL}`, this.nav('cancel')); + Utils.replaceString(dom.querySelector(`#delete-close`), `{MODAL_DELETE}`, this.nav('delete')); + } + + + userProfileModal(dom) { + Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('userTitle')); + Utils.replaceString(dom.querySelector(`#nls-user-high-accuracy`), `{ACCURACY_USER_CHECK}`, this.modal('userAccuracyPref')); + Utils.replaceString(dom.querySelector(`#nls-user-dark-theme`), `{DARK_THEME_CHECK}`, this.modal('darkThemePref')); + Utils.replaceString(dom.querySelector(`#nls-user-debug`), `{DEBUG_USER_CHECK}`, this.modal('userDebugPref')); + Utils.replaceString(dom.querySelector(`#nls-startup-help`), `{STARTUP_HELP}`, this.modal('startupHelp')); + Utils.replaceString(dom.querySelector(`#nls-lang-select`), `{LANG_SELECT}`, this.modal('langPref')); + Utils.replaceString(dom.querySelector(`#nls-lang-fr`), `{LANG_FR}`, this.modal('langFr')); + Utils.replaceString(dom.querySelector(`#nls-lang-en`), `{LANG_EN}`, this.modal('langEn')); + Utils.replaceString(dom.querySelector(`#nls-lang-es`), `{LANG_ES}`, this.modal('langEs')); + Utils.replaceString(dom.querySelector(`#nls-lang-de`), `{LANG_DE}`, this.modal('langDe')); + Utils.replaceString(dom.querySelector(`#nls-lang-it`), `{LANG_IT}`, this.modal('langIt')); + Utils.replaceString(dom.querySelector(`#nls-lang-pt`), `{LANG_PT}`, this.modal('langPt')); + Utils.replaceString(dom.querySelector(`#nls-about-desc`), `{BEERCRACKERZ_DESC}`, this.modal('aboutDesc')); + Utils.replaceString(dom.querySelector(`.nls-user-logout`), `{USER_LOGOUT}`, this.modal('logout')); + Utils.replaceString(dom.querySelector(`#nls-update-pp`), `{UPDATE_PROFILE_PIC_LABEL}`, this.modal('updatePP')); + } + + + updateProfilePictureModal(dom) { + Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('updatePPTitle')); + Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{UPDATE_PP_DESC}`, this.modal('updatePPDesc')); + Utils.replaceString(dom.querySelector(`#update-pp-close`), `{UPDATE_PP_CANCEL}`, this.nav('cancel')); + Utils.replaceString(dom.querySelector(`#update-pp-submit`), `{UPDATE_PP_SUBMIT}`, this.nav('upload')); + } + + + hideShowModal(dom) { + Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-title`), `{MODAL_TITLE}`, this.modal('hideShowTitle')); + Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-spots`), `{SPOTS_HIDESHOW_MODAL}`, this.modal('hideShowSpots')); + Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-shops`), `{SHOPS_HIDESHOW_MODAL}`, this.modal('hideShowShops')); + Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-bars`), `{BARS_HIDESHOW_MODAL}`, this.modal('hideShowBars')); + Utils.replaceString(dom.querySelector(`#nls-view-helper-label`), `{HELPER_LABEL}`, this.modal('hideShowHelperLabel')); + Utils.replaceString(dom.querySelector(`#modal-close-button`), `{MODAL_CLOSE}`, this.nav('close')); + } + + + startupHelpModal(dom) { + Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{MODAL_TITLE}`, this.modal('helpTitle')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-1-1`), `{MODAL_PAGE_1_1}`, this.modal('helpPage1')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-1-2`), `{MODAL_PAGE_1_2}`, this.modal('helpNavNext')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-2-1`), `{MODAL_PAGE_2_1}`, this.modal('helpPage2')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-2-2`), `{MODAL_PAGE_2_2}`, this.modal('helpNavNextPrev')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-3-1`), `{MODAL_PAGE_3_1}`, this.modal('helpPage3')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-3-2`), `{MODAL_PAGE_3_2}`, this.modal('helpNavNextPrev')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-4-1`), `{MODAL_PAGE_4_1}`, this.modal('helpPage4')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-4-2`), `{MODAL_PAGE_4_2}`, this.modal('helpNavNextPrev')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-5-1`), `{MODAL_PAGE_5_1}`, this.modal('helpPage5')); + Utils.replaceString(dom.querySelector(`#nls-modal-page-5-2`), `{MODAL_PAGE_5_2}`, this.modal('helpNavEnd')); + Utils.replaceString(dom.querySelector(`#modal-quit`), `{MODAL_QUIT}`, this.modal('helpQuit')); + Utils.replaceString(dom.querySelector(`#modal-quit-no-see`), `{MODAL_QUIT_NO_SEE}`, this.modal('helpQuitNoSee')); + } + + + markPopup(dom, options) { + Utils.replaceString(dom, `{${options.type.toUpperCase()}_NAME}`, Utils.stripDom(options.name)); + Utils.replaceString(dom, `{${options.type.toUpperCase()}_FINDER}`, options.user); + Utils.replaceString(dom, `{${options.type.toUpperCase()}_FOUND_BY}`, this.popup(`${options.type}FoundBy`)); + Utils.replaceString(dom, `{${options.type.toUpperCase()}_FOUND_WHEN}`, this.popup(`${options.type}FoundWhen`)); + Utils.replaceString(dom, `{${options.type.toUpperCase()}_FOUND_DATE}`, options.date); + Utils.replaceString(dom, `{${options.type.toUpperCase()}_RATE}`, `${options.rate + 1}`); + Utils.replaceString(dom, `{${options.type.toUpperCase()}_DESC}`, options.desc); + } + + + get fullLang() { + return this._fullLang; + } + + +} + + +export default LangManager; diff --git a/front/src/js/ui/MarkPopup.js b/front/src/js/ui/MarkPopup.js new file mode 100644 index 0000000..70a49cb --- /dev/null +++ b/front/src/js/ui/MarkPopup.js @@ -0,0 +1,162 @@ +import VisuHelper from './VisuHelper.js'; +import Utils from '../utils/Utils.js'; +import ColorEnum from '../utils/enums/ColorEnum.js'; +import MapEnum from '../utils/enums/MapEnum.js'; + + +class MarkPopup { + + + constructor(options, cb) { + this._opts = options; + this._popup = null; + this._cb = cb; + this._evtIds = []; + + this._fetchTemplate() + .then(this._init.bind(this)) + .then(this._events.bind(this)) + .finally(this._ready.bind(this)); + } + + + destroy() { + for (let i = 0; i < this._evtIds.length; ++i) { + window.Evts.removeEvent(this._evtIds[i]); + } + + this._opts = null; + this._popup = null; + this._cb = null; + this._evtIds = []; + } + + + _fetchTemplate() { + return new Promise((resolve, reject) => { + window.BeerCrackerz.kom.getTemplate(`/popup/${this._opts.type}`).then(resolve).catch(reject); + }); + } + + + _init(dom) { + return new Promise(resolve => { + this._popup = document.createElement('DIV'); + this._popup.appendChild(dom); + const user = this._opts.user || window.BeerCrackerz.user.username; + const desc = Utils.stripDom(this._opts.description) || window.BeerCrackerz.nls.popup(`${this._opts.type}NoDesc`); + if (!this._opts.creationDate) { // For new marker, we write date of today + this._opts.creationDate = new Date().toISOString().slice(0, 10); + } + const date = new Intl.DateTimeFormat(window.BeerCrackerz.nls.fullLang, { dateStyle: 'long' }).format(new Date(this._opts.creationDate)); + window.BeerCrackerz.nls.markPopup(this._popup, { + type: this._opts.type, + name: this._opts.name, + user: user, + rate: this._opts.rate, + desc: desc, + date: date + }); + // Handle mark types + const types = this._popup.querySelector(`#${this._opts.type}-types`); + for (let i = 0; i < this._opts.types.length; ++i) { + const type = document.createElement('P'); + const icon = document.createElement('IMG'); + type.dataset.type = this._opts.types[i]; + icon.dataset.type = type.dataset.type; + icon.src = `/static/img/logo/${this._opts.types[i]}.svg`; + type.innerHTML = window.BeerCrackerz.nls[this._opts.type](`${this._opts.types[i]}Type`); + type.insertBefore(icon, type.firstChild); + types.appendChild(type); + } + // Handle mark modifiers + const modifiers = this._popup.querySelector(`#${this._opts.type}-modifiers`); + for (let i = 0; i < this._opts.modifiers.length; ++i) { + const modifier = document.createElement('P'); + const icon = document.createElement('IMG'); + modifier.dataset.type = this._opts.modifiers[i]; + icon.dataset.type = modifier.dataset.type; + icon.src = `/static/img/logo/${this._opts.modifiers[i]}.svg`; + modifier.innerHTML = window.BeerCrackerz.nls[this._opts.type](`${this._opts.modifiers[i]}Modifier`); + modifier.insertBefore(icon, modifier.firstChild); + modifiers.appendChild(modifier); + } + // Fill mark rate (rating is in [0, 4] explaining the +1 in loop bound) + const rate = this._popup.querySelector(`#${this._opts.type}-rating`); + for (let i = 0; i < this._opts.rate + 1; ++i) { + rate.children[i].classList.add('active'); + } + // Remove picture icon if user is not in range + const distance = Utils.getDistanceBetweenCoords( + [ window.BeerCrackerz.user.lat, window.BeerCrackerz.user.lng ], + [ this._opts.lat, this._opts.lng ] + ); + if (distance > MapEnum.socialMarkRange) { + this._popup.querySelector('#popup-social').parentNode.removeChild(this._popup.querySelector('#popup-social')); + } + // Remove edition buttons if marker is not user's one, this does not replace a server test for edition... + if (user !== window.BeerCrackerz.user.username) { + if (!window.BeerCrackerz.user.isStaff) { + this._popup.querySelector('#popup-edit').parentNode.removeChild(this._popup.querySelector('#popup-edit')); + } + } + // Append circle around marker + this._opts.color = ColorEnum[this._opts.type]; + this._opts.circle = VisuHelper.drawCircle(this._opts); + this._opts.circle.addTo(window.BeerCrackerz.map); + + this._popupElement = window.L.popup({ + maxWidth: 900 + }).setContent(this._popup); + + resolve(); + }); + } + + + _events() { + // Fire global event on buttons + return new Promise(resolve => { + if (this._opts.user === window.BeerCrackerz.user.username || window.BeerCrackerz.user.isStaff) { + this._evtIds.push(window.Evts.addEvent('click', this._popup.querySelector('#edit-mark'), () => { + window.Evts.publish('editMark', this._opts); + }, this)); + + this._evtIds.push(window.Evts.addEvent('click', this._popup.querySelector('#delete-mark'), () => { + window.Evts.publish('deleteMark', this._opts); + }, this)); + } + this._evtIds.push(window.Evts.addEvent('click', this._popup.querySelector('#center-on'), e => { + e.preventDefault(); + e.stopPropagation(); + window.Evts.publish('centerOn', { + lat: this._opts.lat, + lng: this._opts.lng, + zoom: 17 + }); + }, this)); + + resolve(); + }); + } + + + _ready() { + this._cb(this._popupElement); + } + + + get dom() { + return this._popup; + } + + + get options() { + return this._opts; + } + + +} + + +export default MarkPopup; diff --git a/front/src/js/ui/ModalFactory.js b/front/src/js/ui/ModalFactory.js new file mode 100644 index 0000000..6d4e07b --- /dev/null +++ b/front/src/js/ui/ModalFactory.js @@ -0,0 +1,36 @@ +import AddMarkModal from './modal/AddMarkModal.js'; +import DeleteMarkModal from './modal/DeleteMarkModal.js'; +import EditMarkModal from './modal/EditMarkModal.js'; +import UserModal from './modal/UserModal.js'; +import UpdateProfilePictureModal from './modal/UpdateProfilePictureModal.js'; +import HideShowModal from './modal/HideShowModal.js'; +import StartupHelpModal from './modal/StartupHelpModal.js'; + + +const Classes = { + AddMarkModal, + DeleteMarkModal, + EditMarkModal, + UserModal, + UpdateProfilePictureModal, + HideShowModal, + StartupHelpModal +}; + + +class ModalFactory { + + + static build(name, options = {}) { + if (!Classes[`${name}Modal`]) { + return null; + } + + return new Classes[`${name}Modal`](options); + } + + +} + + +export default ModalFactory; diff --git a/front/src/js/ui/VisuHelper.js b/front/src/js/ui/VisuHelper.js new file mode 100644 index 0000000..dee6d5a --- /dev/null +++ b/front/src/js/ui/VisuHelper.js @@ -0,0 +1,357 @@ +import Utils from '../utils/Utils.js'; +import AccuracyEnum from '../utils/enums/AccuracyEnum.js'; +import ColorEnum from '../utils/enums/ColorEnum.js'; +import MapEnum from '../utils/enums/MapEnum.js'; +import MarkersEnum from '../utils/enums/MarkerEnum.js'; + + +/** + * @class + * @static + * @public +**/ +class VisuHelper { + + + // ======================================================================== // + // ----------------------- Debug interface methods ------------------------ // + // ======================================================================== // + + + /** + * @method + * @name initDebugUI + * @public + * @static + * @memberof VisuHelper + * @author Arthur Beaulieu + * @since November 2022 + * @description + *
        + * Will build the debug UI element and chain them as expected. + *
        + * @return {Element} The debug DOM element for BeerCrackerz + **/ + static initDebugUI() { + const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls); + const debugContainer = document.createElement('DIV'); + const title = document.createElement('H1'); + const userLat = document.createElement('P'); + const userLng = document.createElement('P'); + const updatesAmount = document.createElement('P'); + const userAccuracy = document.createElement('P'); + const highAccuracy = document.createElement('P'); + const maxAge = document.createElement('P'); + const posTimeout = document.createElement('P'); + const zoomLevel = document.createElement('P'); + const marks = document.createElement('P'); + debugContainer.classList.add('debug-container'); + userLat.classList.add('debug-user-lat'); + userLng.classList.add('debug-user-lng'); + updatesAmount.classList.add('debug-updates-amount'); + userAccuracy.classList.add('debug-user-accuracy'); + highAccuracy.classList.add('debug-high-accuracy'); + maxAge.classList.add('debug-pos-max-age'); + posTimeout.classList.add('debug-pos-timeout'); + zoomLevel.classList.add('debug-zoom-level'); + marks.classList.add('debug-marks-amount'); + title.innerHTML = `BeerCrackerz v${window.VERSION}`; + userLat.innerHTML = `${lang('lat')} -`; + userLng.innerHTML = `${lang('lng')} -`; + updatesAmount.innerHTML = `${lang('updates')} 0`; + userAccuracy.innerHTML = `${lang('accuracy')} -`; + highAccuracy.innerHTML = `${lang('highAccuracy')} -`; + maxAge.innerHTML = `${lang('posAge')} -`; + posTimeout.innerHTML = `${lang('posTimeout')} -`; + zoomLevel.innerHTML = `${lang('zoom')} -`; + marks.innerHTML = `${lang('marks')} -`; + debugContainer.appendChild(title); + debugContainer.appendChild(userLat); + debugContainer.appendChild(userLng); + debugContainer.appendChild(updatesAmount); + debugContainer.appendChild(userAccuracy); + debugContainer.appendChild(highAccuracy); + debugContainer.appendChild(maxAge); + debugContainer.appendChild(posTimeout); + debugContainer.appendChild(zoomLevel); + debugContainer.appendChild(marks); + return debugContainer; + } + + + /** + * @method + * @name addDebugUI + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The addDebugUI() method appends the debug DOM element to the document body + *
        + **/ + static addDebugUI() { + document.body.appendChild(window.BeerCrackerz.debugElement); + } + + + /** + * @method + * @name removeDebugUI + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The removeDebugUI() method remove the debug DOM element from the document body + *
        + **/ + static removeDebugUI() { + document.body.removeChild(window.BeerCrackerz.debugElement); + } + + + /** + * @method + * @name updateDebugUI + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The updateDebugUI() method will update informations held in the debug DOM + *
        + **/ + static updateDebugUI() { + if (window.DEBUG === true) { + const options = (Utils.getPreference('map-high-accuracy') === 'true') ? AccuracyEnum.high : AccuracyEnum.optimized; + const element = window.BeerCrackerz.debugElement; + const user = window.BeerCrackerz.user; + const bc = window.BeerCrackerz; + const lang = bc.nls.debug.bind(bc.nls); + const updateSplittedString = element.querySelector('.debug-updates-amount').innerHTML.split(' '); + const updates = parseInt(updateSplittedString[updateSplittedString.length - 1]) + 1; + const marks = bc.marks.spot.length + bc.marks.shop.length + bc.marks.bar.length; + element.querySelector('.debug-user-lat').innerHTML = `${lang('lat')} ${user.lat}`; + element.querySelector('.debug-user-lng').innerHTML = `${lang('lng')} ${user.lng}`; + element.querySelector('.debug-updates-amount').innerHTML = `${lang('updates')} ${updates}`; + element.querySelector('.debug-user-accuracy').innerHTML = `${lang('accuracy')} ${Utils.precisionRound(user.accuracy, 2)}m`; + element.querySelector('.debug-high-accuracy').innerHTML = `${lang('highAccuracy')} ${options.enableHighAccuracy === true ? lang('enabled') : lang('disabled')}`; + element.querySelector('.debug-pos-max-age').innerHTML = `${lang('posAge')} ${options.maximumAge / 1000}s`; + element.querySelector('.debug-pos-timeout').innerHTML = `${lang('posTimeout')} ${options.timeout / 1000}s`; + element.querySelector('.debug-zoom-level').innerHTML = `${lang('zoom')} ${bc.map.getZoom()}`; + element.querySelector('.debug-marks-amount').innerHTML = `${lang('marks')} ${marks}`; + } + } + + + static drawUserMarker() { + if (!window.BeerCrackerz.user.marker) { // Create user marker if not existing + window.BeerCrackerz.user.type = 'user'; + window.BeerCrackerz.user.marker = VisuHelper.addMark(window.BeerCrackerz.user); + // Append circle around marker for accuracy and range for new marker + window.BeerCrackerz.user.radius = window.BeerCrackerz.user.accuracy; + window.BeerCrackerz.user.circle = VisuHelper.drawCircle(window.BeerCrackerz.user); + + window.BeerCrackerz.user.circle.addTo(window.BeerCrackerz.map); + window.BeerCrackerz.user.circle.setStyle({ + opacity: 1, + fillOpacity: 0.1 + }); + // Callback on marker clicked to add marker on user position + window.BeerCrackerz.user.marker.on('click', window.BeerCrackerz.mapClicked.bind(window.BeerCrackerz)); + } else { // Update user marker position, range, and accuracy circle + window.BeerCrackerz.user.marker.setLatLng(window.BeerCrackerz.user); + window.BeerCrackerz.user.circle.setLatLng(window.BeerCrackerz.user); + window.BeerCrackerz.user.circle.setRadius(window.BeerCrackerz.user.accuracy); + } + } + + + static drawCircle(options) { + return window.L.circle(options, { + color: options.color, + fillColor: options.color, + opacity: 0, // This needs to be updated according to user proximity + fillOpacity: 0, // Same for this parameter + radius: options.radius ? options.radius : MapEnum.socialMarkRange, + }); + } + + + static removeMarkDecoration(mark) { + if (mark.popup) { + mark.popup.destroy(); + } + } + + + static addMark(mark) { + let icon = MarkersEnum.black; + if (mark.type === 'shop') { + icon = MarkersEnum.blue; + } else if (mark.type === 'spot') { + icon = MarkersEnum.green; + } else if (mark.type === 'bar') { + icon = MarkersEnum.red; + } else if (mark.type === 'user') { + icon = MarkersEnum.user; + } + + const marker = window.L.marker([mark.lat, mark.lng], { icon: icon }).on('click', VisuHelper.centerOn.bind(VisuHelper, mark)); + if (mark.dom) { + marker.bindPopup(mark.dom); + } + // All markers that are not spot/shop/bar should be appended to the map + if (['spot', 'shop', 'bar'].indexOf(mark.type) === -1) { + marker.addTo(window.BeerCrackerz.map); + } + + return marker; + } + + + /** + * @method + * @name toggleMarkers + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The toggleMarkers() method will, depending on user preference, display or not + * a given mark type. This way, the user can fine tune what is displayed on the map. + * A mark type in spots/shops/bars must be given as an argument + *
        + * @param {String} type - The mark type in spots/tores/bars + **/ + static toggleMarkers(event) { + const type = event.target.dataset.type; + const visible = !(Utils.getPreference(`poi-show-${type}`) === 'true'); + if (visible === true) { + for (let i = 0; i < window.BeerCrackerz.marks[type].length; ++i) { + window.BeerCrackerz.marks[type][i].visible = true; + } + window.BeerCrackerz.map.addLayer(window.BeerCrackerz.clusters[type]); + } else { + for (let i = 0; i < window.BeerCrackerz.marks[type].length; ++i) { + window.BeerCrackerz.marks[type][i].visible = false; + } + window.BeerCrackerz.map.removeLayer(window.BeerCrackerz.clusters[type]); + } + Utils.setPreference(`poi-show-${type}`, visible); + } + + + /** + * @method + * @name toggleDebug + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The toggleDebug() method will, depending on user preference, add or remove + * the debug DOM element to the user interface. The debug DOM display several + * useful information to identify an issue with the geolocation API + *
        + **/ + static toggleDebug() { + const visible = !window.DEBUG; + window.DEBUG = visible; + Utils.setPreference('app-debug', visible); + if (visible) { + VisuHelper.addDebugUI(); + } else { + VisuHelper.removeDebugUI(); + } + } + + + static toggleDarkTheme() { + const isDark = (Utils.getPreference(`dark-theme`) === 'true'); + Utils.setPreference('dark-theme', !isDark); + if (isDark === true) { + document.body.classList.remove('dark-theme'); + document.body.classList.add('light-theme'); + } else { + document.body.classList.remove('light-theme'); + document.body.classList.add('dark-theme'); + } + } + + + /** + * @method + * @name toggleFocusLock + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since January 2022 + * @description + *
        + * The toggleFocusLock() method will, depending on user preference, lock or unlock + * the map centering around the user marker at each position refresh. This way the user + * can roam while the map is following its position. + *
        + **/ + static toggleFocusLock() { + if (Utils.getPreference('map-center-on-user') === 'true') { + window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif(`unlockFocusOn`)); + document.getElementById('center-on').classList.remove('lock-center-on'); + Utils.setPreference('map-center-on-user', 'false'); + } else { + window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif(`lockFocusOn`)); + document.getElementById('center-on').classList.add('lock-center-on'); + window.BeerCrackerz.map.flyTo([window.BeerCrackerz.user.lat, window.BeerCrackerz.user.lng], 18); + Utils.setPreference('map-center-on-user', 'true'); + } + } + + + static centerOn(options) { + // Disable center on lock if previously set to true + if (Utils.getPreference('map-center-on-user') === 'true') { + VisuHelper.toggleFocusLock(); + } + // Actual fly to the marker + let zoom = options.zoom; + if (!options.zoom) { + zoom = window.BeerCrackerz.map.getZoom(); + } + window.BeerCrackerz.map.flyTo([options.lat, options.lng], zoom); + } + + + static toggleStartupHelp() { + if (Utils.getPreference('startup-help') === 'true') { + Utils.setPreference('startup-help', 'false'); + } else { + Utils.setPreference('startup-help', 'true'); + window.location = '/'; // Force reload so user can see the startup help guide + } + } + + + static checkClusteredMark(type) { + const clusters = window.BeerCrackerz.clusters[type] + clusters._featureGroup.eachLayer(layer => { + if (layer instanceof L.MarkerCluster && layer.getChildCount() > 2) { + clusters._showCoverage({ layer: layer }); + } else { + clusters._hideCoverage(); + } + }); + } + + +} + + +export default VisuHelper; + \ No newline at end of file diff --git a/front/src/js/ui/component/ImageResizer.js b/front/src/js/ui/component/ImageResizer.js new file mode 100644 index 0000000..20dfb09 --- /dev/null +++ b/front/src/js/ui/component/ImageResizer.js @@ -0,0 +1,429 @@ +class ImageResizer { + + + constructor(options) { + this._width = options.width; + this._height = options.height; + + this._dom = { + wrapper: options.wrapper, + container: null, + resizer: null, + tl: null, + tr: null, + br: null, + bl: null, + l: null, + t: null, + r: null, + b: null + }; + + this._grab = { + tl: false, + tr: false, + br: false, + bl: false, + l: false, + t: false, + r: false, + b: false, + resizer: false + }; + + this._min = { x: 0, y: 0 }; + this._max = { x: 500, y: 500 }; + this._pointer = { x: 0, y: 0 }; + + this.containerRect = {}; + this.resizerRect = {}; + + this._evtIds = []; + + this._buildUI(); + this._events(); + } + + + destroy() { + for (let i = 0; i < this._evtIds.length; ++i) { + window.Evts.removeEvent(this._evtIds[i]); + } + Object.keys(this).forEach(key => { + delete this[key]; + }); + } + + + _buildUI() { + this._dom.wrapper.classList.add('image-resizer'); + this._dom.container = document.createElement('DIV'); + this._dom.resizer = document.createElement('DIV'); + this._dom.tl = document.createElement('DIV'); + this._dom.tr = document.createElement('DIV'); + this._dom.br = document.createElement('DIV'); + this._dom.bl = document.createElement('DIV'); + this._dom.l = document.createElement('DIV'); + this._dom.t = document.createElement('DIV'); + this._dom.r = document.createElement('DIV'); + this._dom.b = document.createElement('DIV'); + + this._dom.container.classList.add('container'); + this._dom.resizer.classList.add('resizer'); + this._dom.tl.classList.add('tl-grab'); + this._dom.tl.dataset.loc = 'tl'; + this._dom.tr.classList.add('tr-grab'); + this._dom.tr.dataset.loc = 'tr'; + this._dom.br.classList.add('br-grab'); + this._dom.br.dataset.loc = 'br'; + this._dom.bl.classList.add('bl-grab'); + this._dom.bl.dataset.loc = 'bl'; + this._dom.l.classList.add('l-grab'); + this._dom.l.dataset.loc = 'l'; + this._dom.t.classList.add('t-grab'); + this._dom.t.dataset.loc = 't'; + this._dom.r.classList.add('r-grab'); + this._dom.r.dataset.loc = 'r'; + this._dom.b.classList.add('b-grab'); + this._dom.b.dataset.loc = 'b'; + this._dom.resizer.dataset.loc = 'resizer'; + + this._dom.resizer.appendChild(this._dom.tl); + this._dom.resizer.appendChild(this._dom.tr); + this._dom.resizer.appendChild(this._dom.br); + this._dom.resizer.appendChild(this._dom.bl); + this._dom.resizer.appendChild(this._dom.l); + this._dom.resizer.appendChild(this._dom.t); + this._dom.resizer.appendChild(this._dom.r); + this._dom.resizer.appendChild(this._dom.b); + + this._dom.wrapper.appendChild(this._dom.container); + this._dom.wrapper.appendChild(this._dom.resizer); + // Force wrapper to take image aspect ratio (avoid invalid calculus when sending coordinates) + this._dom.wrapper.style.aspectRatio = `${this._width} / ${this._height}`; + // Refresh and init resizer + requestAnimationFrame(() => { + this.containerRect = this._dom.container.getBoundingClientRect(); + this._initResizer(); + this.resizerRect = this._dom.resizer.getBoundingClientRect(); + }); + } + + + _events() { + this._evtIds.push(window.Evts.addEvent('mousedown', this._dom.resizer, this._mouseDown, this)); + this._evtIds.push(window.Evts.addEvent('mousemove', document.body, this._mouseMove, this)); + this._evtIds.push(window.Evts.addEvent('mouseup', this._dom.resizer, this._mouseUp, this)); + this._evtIds.push(window.Evts.addEvent('touchstart', this._dom.resizer, this._mouseDown, this)); + this._evtIds.push(window.Evts.addEvent('touchmove', document.body, this._mouseMove, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._dom.resizer, this._mouseUp, this)); + } + + + _initResizer() { + if (this._width > this._height) { // Landscape + const bRect = this._dom.resizer.getBoundingClientRect(); + this._dom.resizer.style.left = `${(bRect.width - bRect.height) / 2}px`; + this._dom.resizer.style.right = `${(bRect.width - bRect.height) / 2}px`; + } else if (this._width < this._height) { // Portrait + const bRect = this._dom.resizer.getBoundingClientRect(); + this._dom.resizer.style.top = `${(bRect.height - bRect.width) / 2}px`; + this._dom.resizer.style.bottom = `${(bRect.height - bRect.width) / 2}px`; + } + + this._dom.resizer.style.aspectRatio = '1 / 1'; + } + + + _mouseDown(event) { + if (Object.keys(this._grab).indexOf(event.target.dataset.loc) !== -1) { + this._grab[event.target.dataset.loc] = true; + // Need to compute bounding rect before being in mousemove loop + this.containerRect = this._dom.container.getBoundingClientRect(); + this.resizerRect = this._dom.resizer.getBoundingClientRect(); + this._pointer.x = event.pageX - this.containerRect.x; + this._pointer.y = event.pageY - this.containerRect.y; + if (event.touches && event.touches.length) { + this._pointer.x = event.touches[0].clientX - this.containerRect.x; + this._pointer.y = event.touches[0].clientY - this.containerRect.y; + } + } + } + + + _mouseMove(event) { + if (this._isGrabbed()) { + if (event.touches && event.touches.length) { + event.pageX = event.touches[0].clientX; + event.pageY = event.touches[0].clientY; + } + + let offsetX = event.pageX - this.containerRect.x; + let offsetY = event.pageY - this.containerRect.y; + + if (offsetX < 0) { + offsetX = 0; + } + if (offsetX > this.containerRect.width) { + offsetX = this.containerRect.width; + } + if (offsetY < 0) { + offsetY = 0; + } + if (offsetY > this.containerRect.height) { + offsetY = this.containerRect.height; + } + + const offsetL = this.resizerRect.x - this.containerRect.x; + const offsetT = this.resizerRect.y - this.containerRect.y; + const offsetR = this.resizerRect.x + this.resizerRect.width - this.containerRect.x; + const offsetB = this.resizerRect.y + this.resizerRect.height - this.containerRect.y; + const minWidth = this.containerRect.width - (512 * this.containerRect.width / this._width); + const minHeight = this.containerRect.height - (512 * this.containerRect.height / this._height); + + if (this._grab.tl) { // Top/Left + if (offsetT + (offsetX - offsetL) < 0) { // Top blocking + const offset = this._dom.resizer.style.top.slice(0, -2); + this._dom.resizer.style.top = 0; + this._dom.resizer.style.left = `${this._dom.resizer.style.left.slice(0, -2) - offset}px`; + return; + } else if (offsetT + (offsetX - offsetL) > minHeight) { + return; + } + this._dom.resizer.style.left = `${offsetL + (offsetX - offsetL)}px`; + this._dom.resizer.style.top = `${offsetT + (offsetX - offsetL)}px`; + } else if (this._grab.tr) { // Top/Right + if (offsetT + (offsetR - offsetX) < 0) { // Top blocking + const offset = this._dom.resizer.style.top.slice(0, -2); + this._dom.resizer.style.top = 0; + this._dom.resizer.style.right = `${this._dom.resizer.style.right.slice(0, -2) - offset}px`; + return; + } else if (offsetT + (offsetR - offsetX) > minHeight) { + return; + } + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + offsetR - offsetX}px`; + this._dom.resizer.style.top = `${offsetT + (offsetR - offsetX)}px`; + } else if (this._grab.br) { // Bottom/Right + if ((this.containerRect.height - offsetB) + offsetR - offsetX < 0) { // Bottom blocking + const offset = this._dom.resizer.style.bottom.slice(0, -2); + this._dom.resizer.style.bottom = 0; + this._dom.resizer.style.right = `${this._dom.resizer.style.right.slice(0, -2) - offset}px`; + return; + } else if ((this.containerRect.height - offsetB) + offsetR - offsetX > minHeight) { + return; + } + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + offsetR - offsetX}px`; + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + offsetR - offsetX}px`; + } else if (this._grab.bl) { // Bottom/Left + if ((this.containerRect.height - offsetB) + (offsetX - offsetL) < 0) { // Bottom blocking + const offset = this._dom.resizer.style.bottom.slice(0, -2); + this._dom.resizer.style.bottom = 0; + this._dom.resizer.style.left = `${this._dom.resizer.style.left.slice(0, -2) - offset}px`; + return; + } else if ((this.containerRect.height - offsetB) + (offsetX - offsetL) > minHeight) { + return; + } + this._dom.resizer.style.left = `${offsetL + (offsetX - offsetL)}px`; + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + (offsetX - offsetL)}px`; + } else if (this._grab.l) { // Left + if (offsetT + ((offsetX - offsetL) / 2) < 0) { // Top + const offset = this._dom.resizer.style.top.slice(0, -2); + this._dom.resizer.style.top = 0; + this._dom.resizer.style.left = `${this._dom.resizer.style.left.slice(0, -2) - offset}px`; + this._dom.resizer.style.bottom = `${this._dom.resizer.style.bottom.slice(0, -2) - offset}px`; + return; + } else if ((this.containerRect.height - offsetB) + ((offsetX - offsetL) / 2) < 0) { // Bottom + const offset = this._dom.resizer.style.bottom.slice(0, -2); + this._dom.resizer.style.bottom = 0; + this._dom.resizer.style.top = `${this._dom.resizer.style.top.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.left = `${this._dom.resizer.style.left.slice(0, -2) - (offset / 2)}px`; + return; + } else if (offsetL + (offsetX - offsetL) > minWidth) { + return; + } + this._dom.resizer.style.left = `${offsetL + (offsetX - offsetL)}px`; + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + ((offsetX - offsetL) / 2)}px`; + this._dom.resizer.style.top = `${offsetT + ((offsetX - offsetL) / 2)}px`; + } else if (this._grab.t) { // Top + if (offsetL + ((offsetY - offsetT) / 2) < 0) { // Left + const offset = this._dom.resizer.style.left.slice(0, -2); + this._dom.resizer.style.left = 0; + this._dom.resizer.style.right = `${this._dom.resizer.style.right.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.top = `${this._dom.resizer.style.top.slice(0, -2) - (offset / 2)}px`; + return; + } else if ((this.containerRect.width - offsetR) + ((offsetY - offsetT) / 2) < 0) { // Right + const offset = this._dom.resizer.style.right.slice(0, -2); + this._dom.resizer.style.right = 0; + this._dom.resizer.style.top = `${this._dom.resizer.style.top.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.left = `${this._dom.resizer.style.left.slice(0, -2) - (offset / 2)}px`; + return; + } else if (offsetT + (offsetY - offsetT) > minHeight) { + return; + } + this._dom.resizer.style.top = `${offsetT + (offsetY - offsetT)}px`; + this._dom.resizer.style.left = `${offsetL + ((offsetY - offsetT) / 2)}px`; + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + ((offsetY - offsetT) / 2)}px`; + } else if (this._grab.r) { // Right + if (offsetT + (offsetR - offsetX) / 2 < 0) { // Top + const offset = this._dom.resizer.style.top.slice(0, -2); + this._dom.resizer.style.top = 0; + this._dom.resizer.style.right = `${this._dom.resizer.style.right.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.bottom = `${this._dom.resizer.style.bottom.slice(0, -2) - (offset / 2)}px`; + return; + } else if ((this.containerRect.height - offsetB) + (offsetR - offsetX) / 2 < 0) { // Bottom + const offset = this._dom.resizer.style.bottom.slice(0, -2); + this._dom.resizer.style.bottom = 0; + this._dom.resizer.style.top = `${this._dom.resizer.style.top.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.right = `${this._dom.resizer.style.right.slice(0, -2) - (offset / 2)}px`; + return; + } else if ((this.containerRect.width - offsetR) + offsetR - offsetX > minWidth) { + return; + } + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + offsetR - offsetX}px`; + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + (offsetR - offsetX) / 2}px`; + this._dom.resizer.style.top = `${offsetT + (offsetR - offsetX) / 2}px`; + } else if (this._grab.b) { // Bottom + if (offsetL + (offsetB - offsetY) / 2 < 0) { // Left + const offset = this._dom.resizer.style.left.slice(0, -2); + this._dom.resizer.style.left = 0; + this._dom.resizer.style.right = `${this._dom.resizer.style.right.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.bottom = `${this._dom.resizer.style.bottom.slice(0, -2) - (offset / 2)}px`; + return; + } else if ((this.containerRect.width - offsetR) + (offsetB - offsetY) / 2 < 0) { // Right + const offset = this._dom.resizer.style.right.slice(0, -2); + this._dom.resizer.style.right = 0; + this._dom.resizer.style.bottom = `${this._dom.resizer.style.bottom.slice(0, -2) - (offset / 2)}px`; + this._dom.resizer.style.left = `${this._dom.resizer.style.left.slice(0, -2) - (offset / 2)}px`; + return; + } else if ((this.containerRect.height - offsetB) + offsetB - offsetY > minHeight) { + return; + } + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + offsetB - offsetY}px`; + this._dom.resizer.style.left = `${offsetL + (offsetB - offsetY) / 2}px`; + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + (offsetB - offsetY) / 2}px`; + } else if (this._grab.resizer) { // Resizer grabbed + if (this._pointer.x - offsetX > 0) { // Towards left + // Blocking return condition, resizer is on an edge + if (this._dom.resizer.style.left === '0px') { + return; + } + if (offsetL - (this._pointer.x - offsetX) < 0) { + const offset = parseFloat(this._dom.resizer.style.left.slice(0, -2)); + this._dom.resizer.style.left = 0; + this._dom.resizer.style.right = `${parseFloat(this._dom.resizer.style.right.slice(0, -2)) + offset}px`; + } else { + this._dom.resizer.style.left = `${offsetL - (this._pointer.x - offsetX)}px`; + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + (this._pointer.x - offsetX)}px`; + } + } else { // Towards right + // Blocking return condition, resizer is on an edge + if (this._dom.resizer.style.right === '0px') { + return; + } + if ((this.containerRect.width - offsetR) + (this._pointer.x - offsetX) < 0) { + const offset = parseFloat(this._dom.resizer.style.right.slice(0, -2)); + this._dom.resizer.style.right = 0; + this._dom.resizer.style.left = `${parseFloat(this._dom.resizer.style.left.slice(0, -2)) + offset}px`; + } else { + this._dom.resizer.style.left = `${offsetL - (this._pointer.x - offsetX)}px`; + this._dom.resizer.style.right = `${(this.containerRect.width - offsetR) + (this._pointer.x - offsetX)}px`; + } + } + + if (this._pointer.y - offsetY > 0) { // Towards top + // Blocking return condition, resizer is on an edge + if (this._dom.resizer.style.top === '0px') { + return; + } + if (offsetT - (this._pointer.y - offsetY) < 0) { + const offset = parseFloat(this._dom.resizer.style.top.slice(0, -2)); + this._dom.resizer.style.top = 0; + this._dom.resizer.style.bottom = `${parseFloat(this._dom.resizer.style.bottom.slice(0, -2)) + offset}px`; + } else { + this._dom.resizer.style.top = `${offsetT - (this._pointer.y - offsetY)}px`; + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + (this._pointer.y - offsetY)}px`; + } + } else { // Towards bottom + // Blocking return condition, resizer is on an edge + if (this._dom.resizer.style.bottom === '0px') { + return; + } + if ((this.containerRect.height - offsetB) + (this._pointer.y - offsetY) < 0) { + const offset = parseFloat(this._dom.resizer.style.bottom.slice(0, -2)); + this._dom.resizer.style.bottom = 0; + this._dom.resizer.style.top = `${parseFloat(this._dom.resizer.style.top.slice(0, -2)) + offset}px`; + } else { + this._dom.resizer.style.top = `${offsetT - (this._pointer.y - offsetY)}px`; + this._dom.resizer.style.bottom = `${(this.containerRect.height - offsetB) + (this._pointer.y - offsetY)}px`; + } + } + } + } + } + + + _mouseUp() { + this._grab.tl = false; + this._grab.tr = false; + this._grab.br = false; + this._grab.bl = false; + this._grab.l = false; + this._grab.t = false; + this._grab.r = false; + this._grab.b = false; + this._grab.resizer = false; + this._computMinMax(); + } + + + _computMinMax() { + this.containerRect = this._dom.container.getBoundingClientRect(); + this.resizerRect = this._dom.resizer.getBoundingClientRect(); + + this._min = { + x: this.resizerRect.x - this.containerRect.x || 0, + y: this.resizerRect.y - this.containerRect.y || 0 + }; + + this._max = { + x: this.resizerRect.x + this.resizerRect.width - this.containerRect.x || this.containerRect.x, + y: this.resizerRect.y + this.resizerRect.height - this.containerRect.y || this.containerRect.y + }; + } + + + _isGrabbed() { + if (this._grab.tl || this._grab.tr || this._grab.bl || this._grab.br) { + return true; + } else if (this._grab.l || this._grab.t || this._grab.r || this._grab.b) { + return true; + } else if (this._grab.resizer) { + return true; + } + + return false; + } + + + getMinPoint() { + this._computMinMax(); + return { + x: (this._min.x / this.containerRect.width) * this._width, + y: (this._min.y / this.containerRect.height) * this._height + }; + } + + + getMaxPoint() { + this._computMinMax(); + return { + x: (this._max.x / this.containerRect.width) * this._width, + y: (this._max.y / this.containerRect.height) * this._height + }; + } + + +} + + +export default ImageResizer; \ No newline at end of file diff --git a/src/js/ui/Notification.js b/front/src/js/ui/component/Notification.js similarity index 100% rename from src/js/ui/Notification.js rename to front/src/js/ui/component/Notification.js diff --git a/src/js/ui/Rating.js b/front/src/js/ui/component/Rating.js similarity index 84% rename from src/js/ui/Rating.js rename to front/src/js/ui/component/Rating.js index 71ddae1..83c51cf 100644 --- a/src/js/ui/Rating.js +++ b/front/src/js/ui/component/Rating.js @@ -4,7 +4,7 @@ class Rating { constructor(domList, rate) { this._container = null; this._items = []; - this._currentRate = rate || 0; // Mostly for hover operations + this._currentRate = rate || -1; // Mostly for hover operations this._clicked = rate || - 1; // To know when user clicked on a given star this._init(domList); @@ -38,7 +38,7 @@ class Rating { if (event.target.tagName === 'IMG') { this._currentRate = parseInt(event.target.dataset.id); this._container.dataset.rate = this._currentRate; - this.updateStars(); + this.updateVisu(); } } @@ -46,7 +46,7 @@ class Rating { _pointerExit() { this._currentRate = (this._clicked === -1) ? 0 : this._clicked; this._container.dataset.rate = this._currentRate; - this.updateStars(); + this.updateVisu(); } @@ -54,11 +54,11 @@ class Rating { this._currentRate = parseInt(event.target.dataset.id); this._container.dataset.rate = this._currentRate; this._clicked = this._currentRate; - this.updateStars(); + this.updateVisu(); } - updateStars() { + updateVisu() { for (let i = 0; i < this._items.length; ++i) { if (i <= this._currentRate) { this._items[i].classList.add('active'); @@ -73,11 +73,27 @@ class Rating { } + updateRate(rate) { + this.currentRate = rate; + this.clicked = rate; + } + + get currentRate() { return this._currentRate; } + set currentRate(rate) { + this._currentRate = rate; + } + + + set clicked(rate) { + this._clicked = rate; + } + + } diff --git a/front/src/js/ui/component/ZoomSlider.js b/front/src/js/ui/component/ZoomSlider.js new file mode 100644 index 0000000..eaf81ae --- /dev/null +++ b/front/src/js/ui/component/ZoomSlider.js @@ -0,0 +1,91 @@ +class ZoomSlider { + + + constructor(map) { + this._map = map; + this._container = document.querySelector('#zoom-slider'); + this._slider = document.querySelector('#slider-position'); + this._zoomRange = this._map.getMaxZoom() - this._map.getMinZoom(); + this._timeoutId = -1; + this._events(); + } + + + _events() { + // Map callback events + this._map.on('zoomstart', this._zoomStart.bind(this)); + this._map.on('zoomend', this._zoomEnd.bind(this)); + this._map.on('zoom', this._zoom.bind(this)); + // DOM mouse events + this._container.addEventListener('mouseover', this._clearTimeout.bind(this)); + this._container.querySelector('#slider-wrapper').addEventListener('click', this._relativeZoom.bind(this)); + this._container.addEventListener('mouseleave', this._startTimeout.bind(this)); + this._container.querySelector('#zoom-more').addEventListener('click', this._zoomIn.bind(this)); + this._container.querySelector('#zoom-less').addEventListener('click', this._zoomOut.bind(this)); + } + + + _zoomStart() { + this._clearTimeout(); + this._container.classList.add('opened'); + } + + + _zoomEnd() { + const correctedZoom = this._map.getZoom() - this._map.getMinZoom(); + this._slider.style.height = `${(correctedZoom * 100) / this._zoomRange}%`; + this._startTimeout(); + } + + + _zoom() { + this._clearTimeout(); + const correctedZoom = this._map.getZoom() - this._map.getMinZoom(); + this._slider.style.height = `${(correctedZoom * 100) / this._zoomRange}%`; + } + + + _zoomIn() { + this._map.setZoom(this._map.getZoom() + 1); + } + + + _zoomOut() { + this._map.setZoom(this._map.getZoom() - 1); + } + + + _relativeZoom(e) { + this._clearTimeout(); + // y represents the % in slider wrapper (bottom to top) + const bRect = this._container.querySelector('#slider-wrapper').getBoundingClientRect(); + const y = (bRect.height - (e.pageY - bRect.top)) / bRect.height; + // Update height and map zoom level accordingly + this._slider.style.height = `${y * 100}%`; + this._map.setZoom(this._map.getMinZoom() + ((y * 100) * this._zoomRange) / 100); // minZoom + % on zoomRange + } + + + _startTimeout() { + this._timeoutId = setTimeout(() => { + this._container.classList.remove('opened'); + }, 1500); + } + + + _clearTimeout() { + clearTimeout(this._timeoutId); + this._timeoutId = -1; + } + + + hide() { + this._container.classList.remove('opened'); + this._clearTimeout(); + } + + +} + + +export default ZoomSlider; diff --git a/front/src/js/ui/modal/AddMarkModal.js b/front/src/js/ui/modal/AddMarkModal.js new file mode 100644 index 0000000..c955f3f --- /dev/null +++ b/front/src/js/ui/modal/AddMarkModal.js @@ -0,0 +1,41 @@ +import MarkModal from './MarkModal.js'; + + +class AddMarkModal extends MarkModal { + + + constructor(options) { + super('add', options); + } + + + submit(event) { + if (super.submit(event)) { + this._opts.name = this._name.value, + this._opts.description = this._description.value; + this._opts.types = this.getTypes(); + this._opts.modifiers = this.getModifiers(); + this._opts.rate = this._rating.currentRate; + if (this._rootElement.querySelector(`#nls-${this._opts.type}-price`)) { + this._opts.price = this._pricing.currentRate; + } + window.Evts.publish('onMarkAdded', this._opts); + this.close(null, true); + } + } + + + close(event, force) { + if (force === true || event.target.id === 'overlay' || event.target.id.indexOf('close') !== -1) { + // Clear temporary black marker + this._opts.marker.isBeingDefined = false; + this._opts.marker.removeFrom(window.BeerCrackerz.map); + } + super.close(event, force); + } + + +} + + +export default AddMarkModal; diff --git a/front/src/js/ui/modal/BaseModal.js b/front/src/js/ui/modal/BaseModal.js new file mode 100644 index 0000000..ccc9730 --- /dev/null +++ b/front/src/js/ui/modal/BaseModal.js @@ -0,0 +1,142 @@ +class BaseModal { + + + constructor(type) { + /** @private + * @member {string} - The modal type */ + this._type = type; + /** @private + * @member {string} - The HTML template url to fetch */ + this._url = `/modal/${this._type}`; + /** @private + * @member {object} - The template root DOM element */ + this._rootElement = null; + /** @private + * @member {object} - The overlay that contains the modal, full viewport size and close modal on click */ + this._modalOverlay = null; + /** @private + * @member {object} - The close button, in the modal header */ + this._closeButton = null; + /** @private + * @member {array} - The event IDs */ + this._evtIds = []; + + // Modal building sequence: + // - get HTML template from server; + // - parse template response to become DOM object; + // - append DOM element to global overlay; + // - open modal by adding overlay to the body; + // - let child class fill attributes and register its events. + this._loadTemplate(); + } + + + /** @method + * @name destroy + * @public + * @memberof Modal + * @author Arthur Beaulieu + * @since November 2020 + * @description
        This method must be overridden in child class. It only destroys the Modal.js + * properties and close event subscription. The developer must remove its abstracted properties and events after + * calling this method, to make the destruction process complete.
        **/ + destroy() { + for (let i = 0; i < this._evtIds.length; ++i) { + window.Evts.removeEvent(this._evtIds[i]); + } + Object.keys(this).forEach(key => { + delete this[key]; + }); + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ------------------------------------ MODAL INSTANTIATION SEQUENCE ------------------------------------------ */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _loadTemplate + * @private + * @async + * @memberof Modal + * @author Arthur Beaulieu + * @since November 2020 + * @description
        This method creates the modal overlay, fetch the HTML template using the Kom.js + * component, it then build the modal DOM, append it to the overlay, open the modal and call + * _fillAttributes() that must be overridden in the child class. It is asynchronous because of the fetch call, + * so the child class constructor can be fully executed.
        **/ + _loadTemplate() { + window.BeerCrackerz.kom.getTemplate(this._url).then(response => { + // Create DOM from fragment and tweak url to only keep modal type as css class + this._rootElement = response.firstElementChild; + this._rootElement.classList.add(`${this._type}-modal`); + // Create overlay modal container + this._modalOverlay = document.createElement('DIV'); + this._modalOverlay.id = 'overlay'; + this._modalOverlay.classList.add('overlay'); + document.body.appendChild(this._modalOverlay); + // Get close button from template + this._closeButton = this._rootElement.querySelector('#modal-close'); + this.open(); + this._fillAttributes(); // Override in child class to process modal UI + }).catch(error => { + console.error(error); + }); + } + + + /** @method + * @name _fillAttributes + * @private + * @memberof Modal + * @author Arthur Beaulieu + * @since November 2020 + * @description
        This method doesn't implement anything. It must be overridden in child class, to use the + * template DOM elements to build its interactions. It is called once the template is successfully fetched from the + * server.
        **/ + _fillAttributes() { + // Must be overridden in child class to build modal with HTML template attributes + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ------------------------------------ MODAL VISIBILITY MANIPULATION ----------------------------------------- */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + open() { + this._evtIds.push(window.Evts.addEvent('click', this._modalOverlay, this.close, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._modalOverlay, this.close, this)); + this._evtIds.push(window.Evts.addEvent('click', this._closeButton, this.close, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._closeButton, this.close, this)); + this._modalOverlay.appendChild(this._rootElement); + this._modalOverlay.style.display = 'flex'; + setTimeout(() => this._modalOverlay.style.opacity = 1, 50); + } + + + + close(event, force) { + if (event && event.stopPropagation) { + event.stopPropagation(); + } + + if (force === true || event.target.id === 'overlay' || event.target.id.indexOf('close') !== -1) { + if (event && event.type === 'touchend' && event.preventDefault) { + event.preventDefault(); + } + + this._modalOverlay.style.opacity = 0; + setTimeout(() => { + document.body.removeChild(this._modalOverlay); + this.destroy(); + }, 200); + } + } + + +} + + +export default BaseModal; diff --git a/front/src/js/ui/modal/DeleteMarkModal.js b/front/src/js/ui/modal/DeleteMarkModal.js new file mode 100644 index 0000000..92f553d --- /dev/null +++ b/front/src/js/ui/modal/DeleteMarkModal.js @@ -0,0 +1,41 @@ +import BaseModal from './BaseModal.js'; + + +class DeleteMarkModal extends BaseModal { + + + constructor(options) { + super('deletemark'); + this._opts = options; + this._footerCancelButton = null; + this._footerSubmitButton = null; + } + + + _fillAttributes() { + window.BeerCrackerz.nls.deleteMarkModal(this._rootElement); + // The modal doesn't contain any interaction with user inputs + this._footerCancelButton = this._rootElement.querySelector('#cancel-close'); + this._footerSubmitButton = this._rootElement.querySelector('#delete-close'); + this._events(); + } + + + _events() { + this._evtIds.push(window.Evts.addEvent('click', this._footerCancelButton, this.close, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._footerCancelButton, this.close, this)); + this._evtIds.push(window.Evts.addEvent('click', this._footerSubmitButton, this.submit, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._footerSubmitButton, this.submit, this)); + } + + + submit(event) { + event.preventDefault(); + window.Evts.publish('onMarkDeleted', this._opts); + } + + +} + + +export default DeleteMarkModal; diff --git a/front/src/js/ui/modal/EditMarkModal.js b/front/src/js/ui/modal/EditMarkModal.js new file mode 100644 index 0000000..44223ea --- /dev/null +++ b/front/src/js/ui/modal/EditMarkModal.js @@ -0,0 +1,48 @@ +import MarkModal from './MarkModal.js'; + + +class EditMarkModal extends MarkModal { + + + constructor(options) { + super('edit', options); + } + + + _fillAttributes() { + super._fillAttributes(); + // Update modal inputs + this._rootElement.querySelector(`#${this._opts.type}-name`).value = this._opts.name; + this._rootElement.querySelector(`#${this._opts.type}-desc`).value = this._opts.description; + this._rating.updateRate(this._opts.rate); + if (this._opts.price) { + this._pricing.updateRate(this._opts.price); + } + requestAnimationFrame(() => { + this._rating.updateVisu(); + if (this._opts.price) { + this._pricing.updateVisu(); + } + }); + } + + + submit(event) { + if (super.submit(event)) { + this._opts.name = this._name; + this._opts.description = this._description; + this._opts.types = this.getTypes(); + this._opts.modifiers = this.getModifiers(); + this._opts.rating = this._rating.currentRate; + if (this._rootElement.querySelector(`#nls-${this._opts.type}-price`)) { + this._opts.price = this._pricing.currentRate; + } + window.Evts.publish('onMarkEdited', this._opts); + } + } + + +} + + +export default EditMarkModal; diff --git a/front/src/js/ui/modal/HideShowModal.js b/front/src/js/ui/modal/HideShowModal.js new file mode 100644 index 0000000..325cda6 --- /dev/null +++ b/front/src/js/ui/modal/HideShowModal.js @@ -0,0 +1,60 @@ +import BaseModal from './BaseModal.js'; +import VisuHelper from '../../ui/VisuHelper'; +import Utils from '../../utils/Utils.js'; + + +class HideShowModal extends BaseModal { + + + constructor(options) { + super('hideshow'); + this._opts = options; + this._footerCancelButton = null; + this._footerSubmitButton = null; + } + + + _fillAttributes() { + window.BeerCrackerz.nls.hideShowModal(this._rootElement); + + if (Utils.getPreference('poi-show-spot') === 'true') { + this._rootElement.querySelector('#show-spots').checked = true; + } + + if (Utils.getPreference('poi-show-shop') === 'true') { + this._rootElement.querySelector('#show-shops').checked = true; + } + + if (Utils.getPreference('poi-show-bar') === 'true') { + this._rootElement.querySelector('#show-bars').checked = true; + } + + this._footerCancelButton = this._rootElement.querySelector('#cancel-close'); + this._events(); + } + + + _events() { + // Toggles + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#show-spots'), VisuHelper.toggleMarkers, VisuHelper)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#show-shops'), VisuHelper.toggleMarkers, VisuHelper)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#show-bars'), VisuHelper.toggleMarkers, VisuHelper)); + // Labels + this._evtIds.push(window.Evts.addEvent('mouseover', this._rootElement.querySelector('#spots-toggle'), this._updateHelper, this)); + this._evtIds.push(window.Evts.addEvent('mouseover', this._rootElement.querySelector('#shops-toggle'), this._updateHelper, this)); + this._evtIds.push(window.Evts.addEvent('mouseover', this._rootElement.querySelector('#bars-toggle'), this._updateHelper, this)); + this._evtIds.push(window.Evts.addEvent('click', this._footerCancelButton, this.close, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._footerCancelButton, this.close, this)); + } + + + _updateHelper(event) { + const type = event.target.dataset.type; + document.getElementById('nls-viewer-helper').innerHTML = window.BeerCrackerz.nls.modal(`${type}HelperHideShow`) || ''; + } + + +} + + +export default HideShowModal; diff --git a/front/src/js/ui/modal/MarkModal.js b/front/src/js/ui/modal/MarkModal.js new file mode 100644 index 0000000..1ccc1ef --- /dev/null +++ b/front/src/js/ui/modal/MarkModal.js @@ -0,0 +1,165 @@ +import BaseModal from './BaseModal.js'; +import MarkInfosEnum from '../../utils/enums/MarkInfosEnum.js'; +import Rating from '../component/Rating.js'; + + +// Abstraction for addMark and editMark (as they share same content, not values) +class MarkModal extends BaseModal { + + + constructor(action, options) { // action is add/edit, options.type is mark type + super(`${action}${options.type}`); + this._opts = options; // Save mark options + this._action = action; // add/edit + this._name = ''; + this._description = ''; + this._rating = null; + this._pricing = null; + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ------------------------------------ MODAL INSTANTIATION SEQUENCE ------------------------------------------ */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _fillAttributes + * @private + * @memberof AboutModal + * @author Arthur Beaulieu + * @since November 2020 + * @description
        This method doesn't do anything, the about modal is only for reading.
        **/ + _fillAttributes() { + this._opts.user = window.BeerCrackerz.user.username; + // Generic mark modal section + window.BeerCrackerz.nls.addMarkModal(this._rootElement, this._opts.type, this._action); + // Type and modifier handling + const _elementChecked = event => { + if (event.target.closest('p').classList.contains('selected')) { + event.target.closest('p').classList.remove('selected'); + } else { + event.target.closest('p').classList.add('selected'); + } + }; + // Mark title + this._name = this._rootElement.querySelector(`#${this._opts.type}-name`); + // Handle mark types + const types = this._rootElement.querySelector(`#${this._opts.type}-types`); + for (let i = 0; i < MarkInfosEnum[this._opts.type].types.length; ++i) { + const type = document.createElement('P'); + const icon = document.createElement('IMG'); + type.dataset.type = MarkInfosEnum[this._opts.type].types[i]; + icon.dataset.type = type.dataset.type; + icon.src = `/static/img/logo/${MarkInfosEnum[this._opts.type].types[i]}.svg`; + type.innerHTML = window.BeerCrackerz.nls[this._opts.type](`${MarkInfosEnum[this._opts.type].types[i]}Type`); + if (this._opts.types && this._opts.types.indexOf(MarkInfosEnum[this._opts.type].types[i]) !== -1) { + type.classList.add('selected'); + } + type.insertBefore(icon, type.firstChild); + types.appendChild(type); + this._evtIds.push(window.Evts.addEvent('click', type, _elementChecked, this)); + } + // Mark description + this._description = this._rootElement.querySelector(`#${this._opts.type}-desc`); + // Handle mark modifiers + const modifiers = this._rootElement.querySelector(`#${this._opts.type}-modifiers`); + for (let i = 0; i < MarkInfosEnum[this._opts.type].modifiers.length; ++i) { + const modifier = document.createElement('P'); + const icon = document.createElement('IMG'); + modifier.dataset.type = MarkInfosEnum[this._opts.type].modifiers[i]; + icon.dataset.type = modifier.dataset.type; + icon.src = `/static/img/logo/${MarkInfosEnum[this._opts.type].modifiers[i]}.svg`; + modifier.innerHTML = window.BeerCrackerz.nls[this._opts.type](`${MarkInfosEnum[this._opts.type].modifiers[i]}Modifier`); + if (this._opts.modifiers && this._opts.modifiers.indexOf(MarkInfosEnum[this._opts.type].modifiers[i]) !== -1) { + modifier.classList.add('selected'); + } + modifier.insertBefore(icon, modifier.firstChild); + modifiers.appendChild(modifier); + this._evtIds.push(window.Evts.addEvent('click', modifier, _elementChecked, this)); + } + // Rating and pricing if any + this._rating = new Rating(this._rootElement.querySelector(`#${this._opts.type}-rating`)); + if (this._rootElement.querySelector(`#nls-${this._opts.type}-price`)) { + this._pricing = new Rating(this._rootElement.querySelector(`#${this._opts.type}-pricing`)); + } + this._events(); + } + + + /** @method + * @name _events + * @private + * @memberof WishModal + * @author Arthur Beaulieu + * @since November 2020 + * @description
        This method will listen to any click on the submit button to process the textarea + * content to send it to the backend if needed.
        **/ + _events() { + this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector(`#${this._opts.type}-close`), this.close, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector(`#${this._opts.type}-close`), this.close, this)); + this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector(`#${this._opts.type}-submit`), this.submit, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector(`#${this._opts.type}-submit`), this.submit, this)); + } + + + submit(event) { + event.preventDefault(); + let allowed = true; + this._rootElement.querySelector(`#nls-${this._opts.type}-name`).classList.remove('error'); + this._name.classList.remove('error'); + this._rootElement.querySelector(`#nls-${this._opts.type}-type`).classList.remove('error'); + this._rootElement.querySelector(`#nls-${this._opts.type}-rate`).classList.remove('error'); + if (this._rootElement.querySelector(`#nls-${this._opts.type}-price`)) { + this._rootElement.querySelector(`#nls-${this._opts.type}-price`).classList.remove('error'); + } + if (this._name.value === '') { + this._rootElement.querySelector(`#nls-${this._opts.type}-name`).classList.add('error'); + this._name.classList.add('error'); + window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markNameEmpty')); + allowed = false; + } else if (this.getTypes().length === 0) { + this._rootElement.querySelector(`#nls-${this._opts.type}-type`).classList.add('error'); + window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markTypeEmpty')); + allowed = false; + } else if (this._rating.currentRate === -1) { + this._rootElement.querySelector(`#nls-${this._opts.type}-rate`).classList.add('error'); + window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markRateEmpty')); + allowed = false; + } else if (this._pricing !== null && this._pricing.currentRate === -1) { + this._rootElement.querySelector(`#nls-${this._opts.type}-price`).classList.add('error'); + window.BeerCrackerz.notification.raise(window.BeerCrackerz.nls.notif('markPriceEmpty')); + allowed = false; + } + return allowed; + } + + + getTypes() { + const output = []; + const types = this._rootElement.querySelector(`#${this._opts.type}-types`); + for (let i = 0; i < types.children.length; ++i) { + if (types.children[i].classList.contains('selected')) { + output.push(types.children[i].dataset.type); + } + } + return output; + } + + + getModifiers() { + const output = []; + const modifiers = this._rootElement.querySelector(`#${this._opts.type}-modifiers`); + for (let i = 0; i < modifiers.children.length; ++i) { + if (modifiers.children[i].classList.contains('selected')) { + output.push(modifiers.children[i].dataset.type); + } + } + return output; + } + + +} + + +export default MarkModal; diff --git a/front/src/js/ui/modal/StartupHelpModal.js b/front/src/js/ui/modal/StartupHelpModal.js new file mode 100644 index 0000000..f19b518 --- /dev/null +++ b/front/src/js/ui/modal/StartupHelpModal.js @@ -0,0 +1,93 @@ +import BaseModal from './BaseModal.js'; +import Utils from '../../utils/Utils.js'; + + +class StartupHelpModal extends BaseModal { + + + constructor(options) { + super('startuphelp'); + this._opts = options; + this._page = 0; + this._footerQuitButton = null; + this._footerQuitNoSeeButton = null; + } + + + _fillAttributes() { + window.BeerCrackerz.nls.startupHelpModal(this._rootElement); + + this._footerQuitButton = this._rootElement.querySelector('#modal-quit'); + this._footerQuitNoSeeButton = this._rootElement.querySelector('#modal-quit-no-see'); + + this._events(); + } + + + _events() { + this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector('#previous-page'), this._previousPage, this)); + this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector('#next-page'), this._nextPage, this)); + this._evtIds.push(window.Evts.addEvent('click', this._footerQuitButton, this._quitStartupHelp, this)); + this._evtIds.push(window.Evts.addEvent('click', this._footerQuitNoSeeButton, this._quitStartupHelp, this)); + } + + + _previousPage() { + if (this._page > 0) { + this._rootElement.querySelector('#next-page').classList.remove('disabled'); + --this._page; + this._rootElement.querySelectorAll('.page')[this._page].scrollIntoView({ + behavior: 'smooth', + block: 'end', + inline: 'nearest' + }); + if (this._page === 0) { + this._rootElement.querySelector('#previous-page').classList.add('disabled'); + } + this._updatePageCounter(); + } + } + + + _nextPage() { + if (this._page < 5) { + this._rootElement.querySelector('#previous-page').classList.remove('disabled'); + ++this._page; + this._rootElement.querySelectorAll('.page')[this._page].scrollIntoView({ + behavior: 'smooth', + block: 'end', + inline: 'nearest' + }); + if (this._page === 4) { + this._rootElement.querySelector('#next-page').classList.add('disabled'); + } + this._updatePageCounter(); + } + } + + + _updatePageCounter() { + const elements = this._rootElement.querySelector('.page-counter').children; + for (let i = 0; i < elements.length; ++i) { + if (i <= this._page) { + elements[i].classList.add('active'); + } else { + elements[i].classList.remove('active'); + } + } + } + + + _quitStartupHelp(e) { + if (e.target.id === 'modal-quit-no-see') { + Utils.setPreference('startup-help', 'false'); + } + + this.close(null, true); + } + + +} + + +export default StartupHelpModal; diff --git a/front/src/js/ui/modal/UpdateProfilePictureModal.js b/front/src/js/ui/modal/UpdateProfilePictureModal.js new file mode 100644 index 0000000..14d86e4 --- /dev/null +++ b/front/src/js/ui/modal/UpdateProfilePictureModal.js @@ -0,0 +1,57 @@ +import BaseModal from './BaseModal.js'; +import ImageResizer from '../component/ImageResizer.js'; + + +class UpdateProfilePictureModal extends BaseModal { + + + constructor(options) { + super('updatepp'); + this._opts = options; + this._imageResizer = null; + this._footerCancelButton = null; + this._footerSubmitButton = null; + } + + + destroy() { + this._imageResizer.destroy(); + super.destroy(); + } + + + _fillAttributes() { + window.BeerCrackerz.nls.updateProfilePictureModal(this._rootElement); + this._rootElement.querySelector('#wip-pp').src = this._opts.b64; + + this._imageResizer = new ImageResizer({ + wrapper: this._rootElement.querySelector('#wip-pp-wrapper'), + width: this._opts.image.width, + height: this._opts.image.height + }); + // The modal doesn't contain any interaction with user inputs + this._footerCancelButton = this._rootElement.querySelector('#update-pp-close'); + this._footerSubmitButton = this._rootElement.querySelector('#update-pp-submit'); + this._events(); + } + + + _events() { + this._evtIds.push(window.Evts.addEvent('click', this._footerCancelButton, this.close, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._footerCancelButton, this.close, this)); + this._evtIds.push(window.Evts.addEvent('click', this._footerSubmitButton, this.submit, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._footerSubmitButton, this.submit, this)); + } + + + submit(event) { + event.preventDefault(); + this._opts.imageResizer = this._imageResizer; + window.Evts.publish('onProfilePictureUpdated', this._opts); + } + + +} + + +export default UpdateProfilePictureModal; diff --git a/front/src/js/ui/modal/UserModal.js b/front/src/js/ui/modal/UserModal.js new file mode 100644 index 0000000..800c42d --- /dev/null +++ b/front/src/js/ui/modal/UserModal.js @@ -0,0 +1,147 @@ +import BaseModal from './BaseModal.js'; +import VisuHelper from '../VisuHelper.js'; +import DropElement from '../../utils/DropElement.js'; +import Utils from '../../utils/Utils.js'; + + +class UserModal extends BaseModal { + + + constructor(options) { + super('user'); + this._opts = options; + this._footerCancelButton = null; + this._footerSubmitButton = null; + } + + + _fillAttributes() { + window.BeerCrackerz.nls.userProfileModal(this._rootElement); + if (window.BeerCrackerz.user.pp) { + this._rootElement.querySelector(`#user-pp`).src = window.BeerCrackerz.user.pp; + } + + this._rootElement.querySelector(`#user-name`).innerHTML = window.BeerCrackerz.user.username; + this._rootElement.querySelector(`#user-email`).innerHTML = window.BeerCrackerz.user.email; + + new DropElement({ + target: this._rootElement.querySelector('#update-pp-wrapper'), + onDrop: this.updateProfilePicture.bind(this) + }); + new DropElement({ + target: this._rootElement.querySelector('#drop-user-pp'), + onDrop: this.updateProfilePicture.bind(this) + }); + + // Init modal checkbox state according to local storage preferences + if (Utils.getPreference('map-high-accuracy') === 'true') { + this._rootElement.querySelector('#high-accuracy-toggle').checked = true; + } + + if (Utils.getPreference('dark-theme') === 'true') { + this._rootElement.querySelector('#dark-theme-toggle').checked = true; + } + + if (Utils.getPreference('startup-help') === 'true') { + this._rootElement.querySelector('#startup-help-toggle').checked = true; + } + + if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) { + this._rootElement.querySelector('#debug-toggle').checked = true; + } + + const options = this._rootElement.querySelector('#lang-select').getElementsByTagName('option'); + const lang = Utils.getPreference('selected-lang'); + for (let i = 0; i < options.length; ++i) { + if (options[i].value === lang) { + options[i].selected = 'selected'; + } + } + + this._events(); + } + + + /** @method + * @name _events + * @private + * @memberof WishModal + * @author Arthur Beaulieu + * @since November 2020 + * @description
        This method will listen to any click on the submit button to process the textarea + * content to send it to the backend if needed.
        **/ + _events() { + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#high-accuracy-toggle'), window.BeerCrackerz.toggleHighAccuracy, window.BeerCrackerz)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#dark-theme-toggle'), VisuHelper.toggleDarkTheme, VisuHelper)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#debug-toggle'), VisuHelper.toggleDebug, VisuHelper)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#startup-help-toggle'), VisuHelper.toggleStartupHelp, VisuHelper)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#lang-select'), window.BeerCrackerz.updateLang, window.BeerCrackerz)); + this._evtIds.push(window.Evts.addEvent('change', this._rootElement.querySelector('#update-pp'), this.updateProfilePicture, this)); + this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector('#user-pp'), this.updateProfilePicture, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector('#user-pp'), this.updateProfilePicture, this)); + this._evtIds.push(window.Evts.addEvent('click', this._rootElement.querySelector('#logout'), this.logout, this)); + this._evtIds.push(window.Evts.addEvent('touchend', this._rootElement.querySelector('#logout'), this.logout, this)); + } + + + updateProfilePicture(event) { + if (event.type === 'click') { // Clicked on actual pp + const evt = document.createEvent('MouseEvents'); + evt.initEvent('click', true, false); + document.getElementById('update-pp').dispatchEvent(evt); + return; + } + + const fileInput = document.getElementById('update-pp'); + let files = { files: fileInput.files }; // From input change + if (event.files && event.files.length === 1) { // From drop + files = { files: event.files }; + } + + if (files.files && files.files.length === 1) { + // Check if file is conform to what's expected + if (files.files[0].size > 2621440) { // 2.5Mo + document.getElementById('update-pp').value = ''; + document.getElementById('update-pp').classList.add('error'); + document.getElementById('update-pp-error').innerHTML = window.BeerCrackerz.nls.modal('updatePPSizeError'); + return; + } + + if (FileReader) { + const fr = new FileReader(); + fr.onload = () => { + var image = new Image(); + image.src = fr.result; + image.onload = () => { + if (image.width < 512 || image.height < 512) { + document.getElementById('update-pp').value = ''; + document.getElementById('update-pp').classList.add('error'); + document.getElementById('update-pp-error').innerHTML = window.BeerCrackerz.nls.modal('updatePPDimensionError'); + return; + } else { + window.Evts.publish('updateProfile', { + image: image, + b64: fr.result + }); + } + }; + }; + fr.readAsDataURL(files.files[0]); + } else { + console.error('Couldnt read file'); + } + } + } + + + logout() { + window.BeerCrackerz.kom.post('api/auth/logout/', null).then(() => { + window.location = '/welcome' + }); + } + + +} + + +export default UserModal; diff --git a/front/src/js/utils/CustomEvents.js b/front/src/js/utils/CustomEvents.js new file mode 100644 index 0000000..fe6a9ab --- /dev/null +++ b/front/src/js/utils/CustomEvents.js @@ -0,0 +1,452 @@ +import Utils from './Utils.js'; + + +class CustomEvents { + + + /** @summary

        JavaScript regular and custom events abstraction

        + * @author Arthur Beaulieu + * @since June 2020 + * @description
        The CustomEvents class provides an abstraction of JavaScript event listener, to allow + * easy binding and removing those events. It also provides an interface to register custom events. This class is + * meant to be used on all scopes you need ; module or global. Refer to each public method for detailed features. + * For source code, please go to + * https://github.com/ArthurBeaulieu/CustomEvents.js
        + * @param {boolean} [debug=false] - Debug flag ; when true, logs will be output in JavaScript console at each event */ + constructor(debug = false) { + // Prevent wrong type for debug + if (typeof debug !== 'boolean') { + debug = false; + } + /** @private + * @member {boolean} - Internal logging flag from constructor options, allow to output each event action */ + this._debug = debug; + /** @private + * @member {number} - Start the ID incrementer at pseudo random value, used for both regular and custom events */ + this._idIncrementor = (Math.floor(Math.random() * Math.floor(256)) * 5678); + /** @private + * @member {any[]} - We store classical event listeners in array of objects containing all their information */ + this._regularEvents = []; + /** @private + * @member {object} - We store custom events by name as key, each key stores an Array of subscribed events */ + this._customEvents = {}; + /** @public + * @member {string} - Component version */ + this.version = '1.2.1'; + } + + + /** @method + * @name destroy + * @public + * @memberof CustomEvents + * @description
        CustomEvents destructor. Will remove all event listeners and keys in instance.
        */ + destroy() { + // Debug logging + this._raise('log', 'CustomEvents.destroy'); + // Remove all existing eventListener + this.removeAllEvents(); + // Delete object attributes + Utils.removeAllObjectKeys(this); + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* -------------------------------------- CLASSIC JS EVENTS OVERRIDE ------------------------------------------ */ + /* */ + /* The following methods are made to abstract the event listeners from the JavaScript layer, so you can easily */ + /* remove them when done using, without bothering with binding usual business for them. 'addEvent/removeEvent' */ + /* method replace the initial ones. 'removeAllEvents' clears all instance event listeners ; nice for destroy */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name addEvent + * @public + * @memberof CustomEvents + * @description
        addEvent method abstracts the addEventListener method to easily + * remove it when needed, also to set a custom scope on callback.
        + * @param {string} eventName - The event name to fire (mousemove, click, context etc.) + * @param {object} element - The DOM element to attach the listener to + * @param {function} callback - The callback function to execute when event is realised + * @param {object} [scope=element] - The event scope to apply to the callback (optional, default to DOM element) + * @param {object|boolean} [options=false] - The event options (useCapture and else) + * @returns {number|boolean} - The event ID to use to manually remove an event, false if arguments are invalid */ + addEvent(eventName, element, callback, scope = element, options = false) { + // Debug logging + this._raise('log', `CustomEvents.addEvent: ${eventName} ${element} ${callback} ${scope} ${options}`); + // Missing mandatory arguments + if (eventName === null || eventName === undefined || + element === null || element === undefined || + callback === null || callback === undefined) { + this._raise('error', 'CustomEvents.addEvent: Missing mandatory arguments'); + return false; + } + // Prevent wrong type for arguments (mandatory and optional) + const err = () => { + this._raise('error', 'CustomEvents.addEvent: Wrong type for argument'); + }; + // Test argument validity for further process + if (typeof eventName !== 'string' || typeof element !== 'object' || typeof callback !== 'function') { + err(); + return false; + } + if ((scope !== null && scope !== undefined) && typeof scope !== 'object' && typeof scope !== 'function') { + err(); + return false; + } + if ((options !== null && options !== undefined) && (typeof options !== 'object' && typeof options !== 'boolean' && typeof options !== 'string' && typeof options !== 'number')) { + err(); + return false; + } + // Save scope to callback function, default scope is DOM target object + callback = callback.bind(scope); + // Add event to internal array and keep all its data + this._regularEvents.push({ + id: this._idIncrementor, + element: element, + eventName: eventName, + scope: scope, + callback: callback, + options: options + }); + // Add event listener with options + element.addEventListener(eventName, callback, options); + // Post increment to return the true event entry id, then update the incrementer + return this._idIncrementor++; + } + + + /** @method + * @name removeEvent + * @public + * @memberof CustomEvents + * @description
        removeEvent method abstracts the removeEventListener method to + * really remove event listeners.
        + * @param {number} eventId - The event ID to remove listener from. Returned when addEvent is called + * @returns {boolean} - The method status ; true for success, false for non-existing event */ + removeEvent(eventId) { + // Debug logging + this._raise('log', `Events.removeEvent: ${eventId}`); + // Missing mandatory arguments + if (eventId === null || eventId === undefined) { + this._raise('error', 'CustomEvents.removeEvent: Missing mandatory arguments'); + return false; + } + // Prevent wrong type for arguments (mandatory) + if (typeof eventId !== 'number') { + this._raise('error', 'CustomEvents.removeEvent: Wrong type for argument'); + return false; + } + // Returned value + let statusCode = false; // Not found status code by default (false) + // Iterate over saved listeners, reverse order for proper splicing + for (let i = (this._regularEvents.length - 1); i >= 0 ; --i) { + // If an event ID match in saved ones, we remove it and update saved listeners + if (this._regularEvents[i].id === eventId) { + // Update status code + statusCode = true; // Found and removed event listener status code (true) + this._clearRegularEvent(i); + } + } + // Return with status code + return statusCode; + } + + + /** @method + * @name removeAllEvents + * @public + * @memberof CustomEvents + * @description
        Clear all event listener registered through this class object.
        + * @returns {boolean} - The method status ; true for success, false for not removed any event */ + removeAllEvents() { + // Debug logging + this._raise('log', 'CustomEvents.removeAllEvents'); + // Returned value + let statusCode = false; // Didn't removed any status code by default (false) + // Flag to know if there was any previously stored event listeners + const hadEvents = (this._regularEvents.length > 0); + // Iterate over saved listeners, reverse order for proper splicing + for (let i = (this._regularEvents.length - 1); i >= 0; --i) { + this._clearRegularEvent(i); + } + // If all events where removed, update statusCode to success + if (this._regularEvents.length === 0 && hadEvents) { + // Update status code + statusCode = true; // Found and removed all events listener status code (true) + } + // Return with status code + return statusCode; + } + + + /** @method + * @name _clearRegularEvent + * @private + * @memberof CustomEvents + * @description
        _clearRegularEvent method remove the saved event listener for a + * given index in regularEvents array range.
        + * @param {number} index - The regular event index to remove from class attributes + * @return {boolean} - The method status ; true for success, false for not cleared any event */ + _clearRegularEvent(index) { + // Debug logging + this._raise('log', `CustomEvents._clearRegularEvent: ${index}`); + // Missing mandatory arguments + if (index === null || index === undefined) { + this._raise('error', 'CustomEvents._clearRegularEvent: Missing mandatory argument'); + return false; + } + // Prevent wrong type for arguments (mandatory) + if (typeof index !== 'number') { + this._raise('error', 'CustomEvents._clearRegularEvent: Wrong type for argument'); + return false; + } + // Check if index match an existing event in attributes + if (this._regularEvents[index]) { + // Remove its event listener and update regularEvents array + const evt = this._regularEvents[index]; + evt.element.removeEventListener(evt.eventName, evt.callback, evt.options); + this._regularEvents.splice(index, 1); + return true; + } + + return false; + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ------------------------------------------- CUSTOM JS EVENTS ----------------------------------------------- */ + /* */ + /* The three following methods (subscribe, unsubscribe, publish) are designed to reference an event by its name */ + /* and handle as many subscriptions as you want. When subscribing, you get an ID you can use to unsubscribe your */ + /* event later. Just publish with the event name to callback all its registered subscriptions. */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name subscribe + * @public + * @memberof CustomEvents + * @description
        Subscribe method allow you to listen to an event and react when it occurs.
        + * @param {string} eventName - Event name (the one to use to publish) + * @param {function} callback - The callback to execute when event is published + * @param {boolean} [oneShot=false] - One shot : to remove subscription the first time callback is fired + * @returns {number|boolean} - The event id, to be used when manually unsubscribing */ + subscribe(eventName, callback, oneShot = false) { + // Debug logging + this._raise('log', `CustomEvents.subscribe: ${eventName} ${callback} ${oneShot}`); + // Missing mandatory arguments + if (eventName === null || eventName === undefined || + callback === null || callback === undefined) { + this._raise('error', 'CustomEvents.subscribe', 'Missing mandatory arguments'); + return false; + } + // Prevent wrong type for arguments (mandatory and optional) + const err = () => { + this._raise('error', 'CustomEvents.subscribe: Wrong type for argument'); + }; + if (typeof eventName !== 'string' || typeof callback !== 'function') { + err(); + return false; + } + if ((oneShot !== null && oneShot !== undefined) && typeof oneShot !== 'boolean') { + err(); + return false; + } + // Create event entry if not already existing in the registered events + if (!this._customEvents[eventName]) { + this._customEvents[eventName] = []; // Set empty array for new event subscriptions + } + // Push new subscription for event name + this._customEvents[eventName].push({ + id: this._idIncrementor, + name: eventName, + os: oneShot, + callback: callback + }); + // Post increment to return the true event entry id, then update the incrementer + return this._idIncrementor++; + } + + + /** @method + * @name unsubscribe + * @public + * @memberof CustomEvents + * @description
        Unsubscribe method allow you to revoke an event subscription from its string name.
        + * @param {number} eventId - The subscription id returned when subscribing to an event name + * @returns {boolean} - The method status ; true for success, false for non-existing subscription **/ + unsubscribe(eventId) { + // Debug logging + this._raise('log', `CustomEvents.unsubscribe: ${eventId}`); + // Missing mandatory arguments + if (eventId === null || eventId === undefined) { + this._raise('error', 'CustomEvents.unsubscribe: Missing mandatory arguments'); + return false; + } + // Prevent wrong type for arguments (mandatory) + if (typeof eventId !== 'number') { + this._raise('error', 'CustomEvents.unsubscribe: Wrong type for argument'); + return false; + } + // Returned value + let statusCode = false; // Not found status code by default (false) + // Save event keys to iterate properly on this._events Object + const keys = Object.keys(this._customEvents); + // Reverse events iteration to properly splice without messing with iteration order + for (let i = (keys.length - 1); i >= 0; --i) { + // Get event subscriptions + const subs = this._customEvents[keys[i]]; + // Iterate over events subscriptions to find the one with given id + for (let j = 0; j < subs.length; ++j) { + // In case we got a subscription for this events + if (subs[j].id === eventId) { + // Debug logging + this._raise('log', `CustomEvents.unsubscribe: subscription found\n`, subs[j], `\nSubscription n°${eventId} for ${subs.name} has been removed`); + // Update status code + statusCode = true; // Found and unsubscribed status code (true) + // Remove subscription from event Array + subs.splice(j, 1); + // Remove event name if no remaining subscriptions + if (subs.length === 0) { + delete this._customEvents[keys[i]]; + } + // Break since id are unique and no other subscription can be found after + break; + } + } + } + // Return with status code + return statusCode; + } + + + /** @method + * @name unsubscribeAllFor + * @public + * @memberof CustomEvents + * @description
        unsubscribeAllFor method clear all subscriptions registered for given event name.
        + * @param {string} eventName - The event to clear subscription from + * @returns {boolean} - The method status ; true for success, false for non-existing event **/ + unsubscribeAllFor(eventName) { + // Debug logging + this._raise('log', `CustomEvents.unsubscribeAllFor: ${eventName}`); + // Missing mandatory arguments + if (eventName === null || eventName === undefined) { + this._raise('error', 'CustomEvents.unsubscribeAllFor: Missing mandatory arguments'); + return false; + } + // Prevent wrong type for arguments (mandatory and optional) + if (typeof eventName !== 'string') { + this._raise('error', 'CustomEvents.unsubscribeAllFor: Wrong type for argument'); + return false; + } + // Returned value + let statusCode = false; // Not found status code by default (false) + // Save event keys to iterate properly on this._events Object + const keys = Object.keys(this._customEvents); + // Iterate through custom event keys to find matching event to remove + for (let i = 0; i < keys.length; ++i) { + if (keys[i] === eventName) { + // Get event subscriptions + const subs = this._customEvents[keys[i]]; + // Iterate over events subscriptions to find the one with given id, reverse iteration to properly splice without messing with iteration order + for (let j = (subs.length - 1); j >= 0; --j) { + // Update status code + statusCode = true; // Found and unsubscribed all status code (true) + // Remove subscription from event Array + subs.splice(j, 1); + // Remove event name if no remaining subscriptions + if (subs.length === 0) { + delete this._customEvents[keys[i]]; + } + } + } + } + // Return with status code + return statusCode; + } + + + /** @method + * @name publish + * @public + * @memberof CustomEvents + * @description
        Publish method allow you to fire an event by name and trigger all its subscription by callbacks./blockquote> + * @param {string} eventName - Event name (the one to use to publish) + * @param {object} [data=undefined] - The data object to sent through the custom event + * @returns {boolean} - The method status ; true for success, false for non-existing event **/ + publish(eventName, data = null) { + // Debug logging + this._raise('log', `CustomEvents.publish: ${eventName} ${data}`); + // Missing mandatory arguments + if (eventName === null || eventName === undefined) { + this._raise('error', 'CustomEvents.publish: Missing mandatory arguments'); + return false; + } + // Prevent wrong type for arguments (mandatory and optional) + if (typeof eventName !== 'string' || (data !== undefined && typeof data !== 'object')) { + this._raise('error', 'CustomEvents.publish: Wrong type for argument'); + return false; + } + // Returned value + let statusCode = false; // Not found status code by default (false) + // Save event keys to iterate properly on this._events Object + const keys = Object.keys(this._customEvents); + // Iterate over saved custom events + for (let i = 0; i < keys.length; ++i) { + // If published name match an existing events, we iterate its subscriptions. First subscribed, first served + if (keys[i] === eventName) { + // Update status code + statusCode = true; // Found and published status code (true) + // Get event subscriptions + const subs = this._customEvents[keys[i]]; + // Iterate over events subscriptions to find the one with given id + // Reverse subscriptions iteration to properly splice without messing with iteration order + for (let j = (subs.length - 1); j >= 0; --j) { + // Debug logging + this._raise('log', `CustomEvents.publish: fire callback for ${eventName}, subscription n°${subs[j].id}`, subs[j]); + // Fire saved callback + subs[j].callback(data); + // Remove oneShot listener from event entry + if (subs[j].os) { + // Debug logging + this._raise('log', 'CustomEvents.publish: remove subscription because one shot usage is done'); + subs.splice(j, 1); + // Remove event name if no remaining subscriptions + if (subs.length === 0) { + delete this._customEvents[keys[i]]; + } + } + } + } + } + // Return with status code + return statusCode; + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* -------------------------------------------- COMPONENT UTILS ----------------------------------------------- */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _raise + * @private + * @memberof CustomEvents + * @description
        Internal method to abstract console wrapped in debug flag.
        + * @param {string} level - The console method to call + * @param {string} errorValue - The error value to display in console method **/ + _raise(level, errorValue) { + if (this._debug) { + console[level](errorValue); + } + } + + +} + + +export default CustomEvents; diff --git a/front/src/js/utils/DropElement.js b/front/src/js/utils/DropElement.js new file mode 100644 index 0000000..a6af52e --- /dev/null +++ b/front/src/js/utils/DropElement.js @@ -0,0 +1,202 @@ +import Utils from './Utils.js'; + + +class DropElement { + + + /** @summary

        Make any DOM element drop friendly

        + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This class will make any DOM element able to receive drop event. It propose an overlay + * when the target is hovered with a draggable element. It handle both the desktop and the mobile behavior. It must be + * used with a DragElement class for perfect compatibility!
        + * @param {object} options - The element to drop options + * @param {object} options.target - The element to allow dropping in **/ + constructor(options) { + /** @private + * @member {object} - The element to make allow dropping in */ + this._target = options.target; // Get given target from the DOM + /** @private + * @member {function} - The callback function to call on each drop event */ + this._onDropCB = options.onDrop; + /** @private + * @member {number[]} - The event IDs for all mobile and desktop dropping events */ + this._evtIds = []; + /** @private + * @member {number} - This counter helps to avoid enter/leave events to overlap when target has children */ + this._movementCounter = 0; + /** @private + * @member {string} - The transparent border that must be added to avoid weird target resize on hover */ + this._transparentBorder = ''; + // Build DOM elements and subscribe to drag events + this._buildElements(); + this._events(); + } + + + /** @method + * @name destroy + * @public + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will unsubscribe all drop events and remove all properties.
        **/ + destroy() { +// Utils.clearAllEvents(this._evtIds); + Utils.removeAllObjectKeys(this); + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* --------------------------------- DROPELEMENT INSTANTIATION SEQUENCE --------------------------------------- */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _buildElements + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will define the transparent border style and append this virtual border to the + * target DOM element.
        **/ + _buildElements() { + this._transparentBorder = 'dashed 3px transparent'; + this._target.style.border = this._transparentBorder; + } + + + /** @method + * @name _events + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will subscribe to drop events, both for desktop and mobile.
        **/ + _events() { + this._target.addEventListener('dragenter', this._dragEnter.bind(this)); + this._target.addEventListener('dragover', this._dragOver.bind(this)); + this._target.addEventListener('dragleave', this._dragLeave.bind(this)); + this._target.addEventListener('drop', this._drop.bind(this)); + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ----------------------------------- DESKTOP DROP EVENTS METHODS -------------------------------------------- */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _dragEnter + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will handle the entering of a dragged div over the target DOM element. When + * the target DOM element is hovered, a dashed border is made visible, replacing the transparent one to notify the + * user that the dragged div can be dropped.
        + * @param {object} event - The mouse event **/ + _dragEnter(event) { + this._eventBehavior(event); + ++this._movementCounter; + this._target.style.border = 'dashed 3px #ffa800'; + } + + + /** @method + * @name _dragOver + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will handle the dragged div hovering the target DOM element.
        + * @param {object} event - The mouse event **/ + _dragOver(event) { + this._eventBehavior(event); + event.dataTransfer.dropEffect = 'copy'; + } + + + /** @method + * @name _dragLeave + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will handle the event that is fired when the hovered div leaves the target + * DOM element. It require the movement counter to be equal to zero to restore the transparent border of the target + * DOM element.
        + * @param {object} event - The mouse event **/ + _dragLeave(event) { + this._eventBehavior(event); + --this._movementCounter; + if (this._movementCounter === 0) { + this._target.style.border = this._transparentBorder; + } + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ------------------------------ MOBILE AND DESKTOP DROP EVENTS METHODS -------------------------------------- */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _drop + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will handle the dropping of a DragElement, to properly read the data it holds + * and send it to the drop callback provided in constructor.
        + * @param {object} event - The mouse or touch event **/ + _drop(event) { + this._eventBehavior(event); + this._target.style.border = this._transparentBorder; + this._onDropCB(event.dataTransfer); + } + + + /* --------------------------------------------------------------------------------------------------------------- */ + /* ------------------------------------------- UTILS METHODS -------------------------------------------------- */ + /* --------------------------------------------------------------------------------------------------------------- */ + + + /** @method + * @name _eventBehavior + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will prevent the default behavior of given event, and will stop its + * propagation.
        + * @param {object} event - The mouse or touch event **/ + _eventBehavior(event) { + event.preventDefault(); + event.stopPropagation(); + } + + + /** @method + * @name _isTouchEventInTarget + * @private + * @memberof DropElement + * @author Arthur Beaulieu + * @since December 2020 + * @description
        This method will compare a touch point to the target position and return true if the + * touch point is inside the target DOM element.
        + * @param {object} touchPosition - The touch event + * @return {boolean} Do the touch point is included in the target DOM element **/ + _isTouchEventInTarget(touchPosition) { + const rect = this._target.getBoundingClientRect(); + const inAxisX = touchPosition.pageX >= rect.x && (touchPosition.pageX <= rect.x + rect.width); + const inAxisY = touchPosition.pageY >= rect.y && (touchPosition.pageY <= rect.y + rect.height); + return (inAxisX && inAxisY); + } + + + } + + + export default DropElement; + \ No newline at end of file diff --git a/front/src/js/utils/Utils.js b/front/src/js/utils/Utils.js new file mode 100644 index 0000000..4028674 --- /dev/null +++ b/front/src/js/utils/Utils.js @@ -0,0 +1,283 @@ +/** + * @class + * @static + * @public +**/ +class Utils { + + + /** + * @method + * @name stripDom + * @public + * @static + * @memberof Utils + * @author Arthur Beaulieu + * @since November 2022 + * @description + *
        + * From a given string/number input, this method will strip all unnecessary + * characters and will only retrun the text content as a string. + *
        + * @param {String|Number} html - The html string to strip + * @return {String} The stripped text content, empty string on error + **/ + static stripDom(html) { + // Not accepting empty or not string/number + if (!html || (typeof html !== 'string' && typeof html !== 'number')) { + return ''; + } + + const doc = new DOMParser().parseFromString(html, 'text/html'); + return doc.body.textContent || ''; + } + + + /** + * @method + * @name replaceString + * @public + * @static + * @memberof Utils + * @author Arthur Beaulieu + * @since November 2022 + * @description + *
        + * Replace a given string in an HTML element with another. + *
        + * @param {Element} element - The DOM element to replace string in + * @param {String} string - The string to be replaced + * @param {String} value - The value to apply to the replaced string + * @return {Boolean} The success status of the replace action + **/ + static replaceString(element, string, value) { + if (!element || !element.innerHTML || !string || typeof string !== 'string' || !value || typeof value !== 'string') { + return false; + } + + element.innerHTML = element.innerHTML.replace(string, value); + return true; + } + + + /** + * @method + * @name getDistanceBetweenCoords + * @public + * @static + * @memberof Utils + * @author Arthur Beaulieu + * @since November 2022 + * @description + *
        + * Compute the distance in meters between two points given in [Lat, Lng] arrays. + *
        + * @param {Array} from - The first point lat and lng array + * @param {Array} to - The second point lat and lng array + * @return {Number} A floating number, the distance between two points given in meters + **/ + static getDistanceBetweenCoords(from, to) { + // Generic argument testing + if (!from || !to || !Array.isArray(from) || !Array.isArray(to)) { + return -1; + } + // From input array testing + if (from.length !== 2 || typeof from[0] !== 'number' || typeof from[1] !== 'number') { + return -1; + } + // To input array testing + if (to.length !== 2 || typeof to[0] !== 'number' || typeof to[1] !== 'number') { + return -1; + } + // Return distance in meters + const lat1 = (from[0] * Math.PI) / 180; + const lon1 = (from[1] * Math.PI) / 180; + const lat2 = (to[0] * Math.PI) / 180; + const lon2 = (to[1] * Math.PI) / 180; + // Delta between coords to compute output distance + const deltaLat = lat2 - lat1; + const deltaLon = lon2 - lon1; + const a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2); + const c = 2 * Math.asin(Math.sqrt(a)); + return (c * 6371000); // Earth radius in meters + } + + + /** + * @method + * @name precisionRound + * @public + * @memberof Utils + * @author Arthur Beaulieu + * @since September 2018 + * @description + *
        + * Do a Math.round with a given precision (ie amount of integers after the coma). + *
        + * @param {Nunmber} value - The value to precisely round (> 0) + * @param {Number} precision - The number of integers after the coma (> 0) + * @return {Number} - The rounded value + **/ + static precisionRound(value, precision) { + if (typeof value !== 'number' || typeof precision !== 'number') { + return -1; + } + const multiplier = Math.pow(10, precision || 0); + return Math.round(value * multiplier) / multiplier; + } + + + /** + * @method + * @name setDefaultPreferences + * @public + * @static + * @memberof Utils + * @author Arthur Beaulieu + * @since November 2022 + * @description + *
        + * Analyze preferences and fallback to default values if preferences doesn't exists. + *
        + **/ + static setDefaultPreferences() { + if (Utils.getPreference('poi-show-spot') === null) { + Utils.setPreference('poi-show-spot', true); + } + + if (Utils.getPreference('poi-show-shop') === null) { + Utils.setPreference('poi-show-shop', true); + } + + if (Utils.getPreference('poi-show-bar') === null) { + Utils.setPreference('poi-show-bar', true); + } + + if (Utils.getPreference('map-plan-layer') === null) { + Utils.setPreference('map-plan-layer', 'Plan OSM'); + } + + if (Utils.getPreference('selected-lang') === null) { + Utils.setPreference('selected-lang', 'en'); + } + + if (Utils.getPreference('app-debug') === null) { + Utils.setPreference('app-debug', false); + } + + if (Utils.getPreference('map-high-accuracy') === null) { + Utils.setPreference('map-high-accuracy', false); + } + + if (Utils.getPreference('map-center-on-user') === null) { + Utils.setPreference('map-center-on-user', false); + } + + if (Utils.getPreference('dark-theme') === null) { + Utils.setPreference('dark-theme', true); + } + + if (Utils.getPreference('startup-help') === null) { + Utils.setPreference('startup-help', true); + } + } + + + /** + * @method + * @name formatMarker + * @public + * @memberof BeerCrackerz + * @author Arthur Beaulieu + * @since February 2022 + * @description + *
        + * This method formats a mark so it can be parsed using JSON.parse + * in order to be later stored in database. + *
        + * @param {Object} mark - The mark options to format for server communication + * @return {Object} The formatted mark + **/ + static formatMarker(mark) { + // Mandatory arguments + if (!mark || !mark.name || !mark.types || !mark.lat || !mark.lng) { + return null; + } + // Mandatory arguments proper types + if (typeof mark.name !== 'string' || !Array.isArray(mark.types) || typeof mark.lat !== 'number' || typeof mark.lng !== 'number') { + return null; + } + // Only return if types aren't all strings + for (let i = 0; i < mark.types.length; ++i) { + if (typeof mark.types[i] !== 'string') { + return null; + } + } + // Only return if description is not properly formated + if (mark.description && typeof mark.description !== 'string') { + return null; + } + // Only return if modifiers are not properly formated + if (mark.modifiers) { + if (!Array.isArray(mark.modifiers)) { + return null; + } + + for (let i = 0; i < mark.modifiers.length; ++i) { + if (typeof mark.modifiers[i] !== 'string') { + return null; + } + } + } + // Only return if rate is not a number or not between 0 and 4 + if (mark.rate && typeof mark.rate !== 'number' || mark.rate < 0 || mark.rate > 4) { + return null; + } + // Only return if price is not a number or not between 0 and 2 + if (mark.price && typeof mark.price !== 'number' || mark.price < 0 || mark.price > 2) { + return null; + } + // Finally return formatted mark + return { + name: mark.name, + types: mark.types, + lat: mark.lat, + lng: mark.lng, + description: mark.description, + modifiers: mark.modifiers, + rate: mark.rate, + price: mark.price + }; + } + + + static removeAllObjectKeys(obj) { + if (!obj || typeof obj !== 'object' || Array.isArray(obj)) { + return false; + } + + Object.keys(obj).forEach(key => { + delete obj[key]; + }); + + return true; + } + + + /* Preference get set (DEPRECATED, will be mgrated with user pref when ready) */ + + + static getPreference(pref) { + return localStorage.getItem(pref) || null; + } + + + static setPreference(pref, value) { + localStorage.setItem(pref, value); + } + + +} + + +export default Utils; diff --git a/front/src/js/utils/enums/AccuracyEnum.js b/front/src/js/utils/enums/AccuracyEnum.js new file mode 100644 index 0000000..1dbbc90 --- /dev/null +++ b/front/src/js/utils/enums/AccuracyEnum.js @@ -0,0 +1,12 @@ +export default Object.freeze({ + optimized: { + enableHighAccuracy: false, // Less consuption + maximumAge: 30000, // A position will last 30s maximum + timeout: 29000 // A position is updated in 29s maximum + }, + high: { + enableHighAccuracy: true, // More consuption, better position + maximumAge: 1000, // A position will last 1s maximum + timeout: 900, // A position is updated in 0.9s maximum + } +}); diff --git a/front/src/js/utils/enums/ClusterEnum.js b/front/src/js/utils/enums/ClusterEnum.js new file mode 100644 index 0000000..987f497 --- /dev/null +++ b/front/src/js/utils/enums/ClusterEnum.js @@ -0,0 +1,71 @@ +export default Object.freeze({ + spot: new window.L.MarkerClusterGroup({ + animateAddingMarkers: true, + disableClusteringAtZoom: 18, + showCoverageOnHover: false, + spiderfyOnMaxZoom: false, + maxClusterRadius: 0, + iconCreateFunction: cluster => { + return window.L.divIcon({ + className: 'cluster-icon-wrapper', + html: ` + + ${cluster.getChildCount()} + ` + }); + }, + polygonOptions: { + fillColor: '#3ABC30', + color: '#3ABC30', + weight: 0.5, + opacity: 1, + fillOpacity: 0.3 + } + }), + shop: new window.L.MarkerClusterGroup({ + animateAddingMarkers: true, + disableClusteringAtZoom: 18, + showCoverageOnHover: false, + spiderfyOnMaxZoom: false, + maxClusterRadius: 0, + iconCreateFunction: cluster => { + return window.L.divIcon({ + className: 'cluster-icon-wrapper', + html: ` + + ${cluster.getChildCount()} + ` + }); + }, + polygonOptions: { + fillColor: '#4295CF', + color: '#4295CF', + weight: 0.5, + opacity: 1, + fillOpacity: 0.3 + } + }), + bar: new window.L.MarkerClusterGroup({ + animateAddingMarkers: true, + disableClusteringAtZoom: 18, + showCoverageOnHover: false, + spiderfyOnMaxZoom: false, + maxClusterRadius: 0, + iconCreateFunction: cluster => { + return window.L.divIcon({ + className: 'cluster-icon-wrapper', + html: ` + + ${cluster.getChildCount()} + ` + }); + }, + polygonOptions: { + fillColor: '#D0465D', + color: '#D0465D', + weight: 0.5, + opacity: 1, + fillOpacity: 0.3 + } + }) +}); diff --git a/front/src/js/utils/enums/ColorEnum.js b/front/src/js/utils/enums/ColorEnum.js new file mode 100644 index 0000000..117c3d9 --- /dev/null +++ b/front/src/js/utils/enums/ColorEnum.js @@ -0,0 +1,7 @@ +export default Object.freeze({ + newMarkRange: '#FFD87D', + user: '#63FFF5', + spot: '#26AD23', + shop: '#247DC9', + bar: '#CA2A3D' +}); diff --git a/front/src/js/utils/enums/MapEnum.js b/front/src/js/utils/enums/MapEnum.js new file mode 100644 index 0000000..a569fbe --- /dev/null +++ b/front/src/js/utils/enums/MapEnum.js @@ -0,0 +1,8 @@ +export default Object.freeze({ + newMarkRange: 200000000/*200*/, // TODO fallback to 200 when roles are implement server side + socialMarkRange: 100, + mapBounds: window.L.latLngBounds( + window.L.latLng(-89.98155760646617, -180), + window.L.latLng(89.99346179538875, 180) + ) +}); diff --git a/front/src/js/utils/enums/MarkInfosEnum.js b/front/src/js/utils/enums/MarkInfosEnum.js new file mode 100644 index 0000000..1af4c9b --- /dev/null +++ b/front/src/js/utils/enums/MarkInfosEnum.js @@ -0,0 +1,14 @@ +export default Object.freeze({ + spot: { + types: ['forest', 'river', 'cliff', 'mountain', 'beach', 'sea', 'city', 'pov', 'lake'], + modifiers: ['bench', 'covered', 'toilet', 'store', 'trash', 'parking'] + }, + shop: { + types: ['store', 'super', 'hyper', 'cellar'], + modifiers: ['bio', 'craft', 'fresh', 'card', 'choice'] + }, + bar: { + types: ['regular', 'snack', 'cellar', 'rooftop'], + modifiers: ['tobacco', 'food', 'card', 'choice', 'outdoor'] + } +}); diff --git a/front/src/js/utils/enums/MarkerEnum.js b/front/src/js/utils/enums/MarkerEnum.js new file mode 100644 index 0000000..f2a96e1 --- /dev/null +++ b/front/src/js/utils/enums/MarkerEnum.js @@ -0,0 +1,37 @@ +export default Object.freeze({ + blue: new window.L.Icon({ + iconUrl: '/static/img/marker/marker-icon-blue.png', + iconSize: [25, 42], + iconAnchor: [12, 42], + popupAnchor: [1, -34], + shadowSize: [42, 42], + }), + red: new window.L.Icon({ + iconUrl: '/static/img/marker/marker-icon-red.png', + iconSize: [25, 42], + iconAnchor: [12, 42], + popupAnchor: [1, -34], + shadowSize: [42, 42], + }), + green: new window.L.Icon({ + iconUrl: '/static/img/marker/marker-icon-green.png', + iconSize: [25, 42], + iconAnchor: [12, 42], + popupAnchor: [1, -34], + shadowSize: [42, 42], + }), + black: new window.L.Icon({ + iconUrl: '/static/img/marker/marker-icon-black.png', + iconSize: [25, 42], + iconAnchor: [12, 42], + popupAnchor: [1, -34], + shadowSize: [42, 42], + }), + user: new window.L.Icon({ + iconUrl: '/static/img/marker/user-position.png', + iconSize: [32, 32], + iconAnchor: [16, 16], + popupAnchor: [1, -34], + shadowSize: [32, 32], + }), +}); diff --git a/front/src/js/utils/enums/ProviderEnum.js b/front/src/js/utils/enums/ProviderEnum.js new file mode 100644 index 0000000..0f320f2 --- /dev/null +++ b/front/src/js/utils/enums/ProviderEnum.js @@ -0,0 +1,14 @@ +export default Object.freeze({ + planOsm: window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap', + minZoom: 2, + maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19 + maxZoom: 21, + }), + satEsri: window.L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { + attribution: '© Esri Imagery', + minZoom: 2, + maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19 + maxZoom: 21 + }) +}); diff --git a/front/src/js/utils/enums/SupportedLangEnum.js b/front/src/js/utils/enums/SupportedLangEnum.js new file mode 100644 index 0000000..731ef2e --- /dev/null +++ b/front/src/js/utils/enums/SupportedLangEnum.js @@ -0,0 +1,3 @@ +export default Object.freeze( + ['en', 'fr', 'es', 'de', 'pt'] +); diff --git a/front/src/scss/component/_imageresizer.scss b/front/src/scss/component/_imageresizer.scss new file mode 100644 index 0000000..8dd12ff --- /dev/null +++ b/front/src/scss/component/_imageresizer.scss @@ -0,0 +1,90 @@ +.image-resizer { + display: block; + + img { + user-select: none; + } + + .container { + height: 100%; + position: absolute; + top: 0; + width: 100%; + } + + .resizer { + border: solid 3px var(--color-bg-a1); + bottom: 0; + box-shadow: 0px 0px 500px 500px rgba(86, 68, 40, .7); + display: flex; + left: 0; + position: absolute; + resize: both; + right: 0; + top: 0; + + .tl-grab, .tr-grab, .br-grab, .bl-grab { + background-color: #c99458; + height: 9px; + position: absolute; + width: 9px; + } + + .tl-grab { + cursor: nwse-resize; + left: -3px; + top: -3px; + } + .tr-grab { + cursor: nesw-resize; + right: -3px; + top: -3px; + } + .br-grab { + cursor: nwse-resize; + right: -3px; + bottom: -3px; + } + .bl-grab { + cursor: nesw-resize; + left: -3px; + bottom: -3px; + } + + .l-grab, .r-grab { + background-color: #ffa800; + cursor: ew-resize; + height: calc(100% - 12px); + position: absolute; + width: 3px; + } + + .t-grab, .b-grab { + background-color: #ffa800; /* rgba(255, 168, 40, .7) #ffa800 */ + cursor: ns-resize; + height: 3px; + position: absolute; + width: calc(100% - 12px); + } + + .l-grab { + left: -3px; + top: 6px; + } + + .t-grab { + top: -3px; + left: 6px; + } + + .r-grab { + right: -3px; + top: 6px; + } + + .b-grab { + bottom: -3px; + right: 6px; + } + } +} diff --git a/front/src/scss/component/_notification.scss b/front/src/scss/component/_notification.scss new file mode 100644 index 0000000..ef40cba --- /dev/null +++ b/front/src/scss/component/_notification.scss @@ -0,0 +1,28 @@ +.notification-wrapper { + align-items: center; + background-color: var(--color-bg); + border-radius: .5rem; + box-shadow: 0 0 1rem var(--color-bg-a5); + display: flex; + flex-direction: column; + height: 3rem; + justify-content: center; + left: calc(50% - 15rem); + opacity: 0; + position: absolute; + top: -4rem; + width: 30rem; + z-index: 40; + + transition: top .3s, opacity .2s ease-out; + + &.opened { + opacity: 1; + top: 1rem; + } + + .notification-message { + font-style: italic; + margin: 0; + } +} diff --git a/front/src/scss/component/_zoomslider.scss b/front/src/scss/component/_zoomslider.scss new file mode 100644 index 0000000..a5c3780 --- /dev/null +++ b/front/src/scss/component/_zoomslider.scss @@ -0,0 +1,67 @@ +.zoom-slider { + align-items: center; + + background: var(--color-bg); + background-clip: padding-box; + border: 2px solid var(--color-bg-a1); + border-radius: .5rem; + box-shadow: 0 0 1rem var(--color-bg-a9); + display: flex; + flex-direction: column; + height: 20rem; + justify-content: center; + opacity: 0; + position: absolute; + right: -6rem; + top: calc(50% - 10rem); + width: 4.8rem; + z-index: 20; + + transition: right var(--transition-x2), opacity var(--transition-x2); + + &.opened { + opacity: 1; + right: 1rem; + } + + p { + cursor: pointer; + font-size: var(--font-size-l3); + font-weight: bold; + margin: .5rem 0; + user-select: none; + + transition: .2s color; + + &:last-child { + margin-bottom: 0; + } + + &:active, + &:focus, + &:hover { + color: var(--color-primary); + } + } + + .slider-wrapper { + background-color: var(--color-txt); + border-radius: .5rem; + box-shadow: 0 0 1rem var(--color-bg); + cursor: pointer; + height: 80%; + overflow: hidden; + position: relative; + width: 20%; + + .slider-position { + background-color: var(--color-primary); + bottom: 0; + border-top: solid 1px var(--color-primary-a5); + position: absolute; + width: 100%; + + transition: height .2s ease-in; + } + } +} diff --git a/front/src/scss/modal/_hideshow.scss b/front/src/scss/modal/_hideshow.scss new file mode 100644 index 0000000..3b02ff3 --- /dev/null +++ b/front/src/scss/modal/_hideshow.scss @@ -0,0 +1,49 @@ +.hide-show-modal { + .view-options-wrapper { + display: flex; + + .view-options { + border-right: solid 1px var(--color-txt-a5); + width: 20rem; + } + + .view-helper { + margin-left: 1rem; + text-align: left; + width: 20rem; + + p:first-child { + font-style: italic; + text-align: center; + margin-bottom: var(--font-size-l4); + } + + p:last-child { + text-indent: var(--font-size-l4); + } + } + } + + .item { + align-items: center; + display: flex; + justify-content: space-between; + margin: 1rem auto; + max-width: 15rem; + + img { + margin-right: var(--font-size-l4); + height: var(--font-size-l4); + } + + label { + margin-bottom: 0; + margin-right: var(--font-size-l4); + } + + input { + margin: 0; + width: auto; + } + } +} diff --git a/front/src/scss/modal/_mark.scss b/front/src/scss/modal/_mark.scss new file mode 100644 index 0000000..0bb82a9 --- /dev/null +++ b/front/src/scss/modal/_mark.scss @@ -0,0 +1,78 @@ +.new-mark-modal, +.edit-mark-modal { + width: calc(100% - 2rem); + + h1, p { + text-align: center; + } + + .types, .modifiers { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: .5rem auto var(--font-size); + + p { + align-items: center; + border: solid 1px var(--color-txt); + border-radius: .5rem; + color: var(--color-txt); + cursor: pointer; + display: flex; + flex-direction: row; + font-size: 1.2rem; + justify-content: center; + margin: .5rem; + padding: .25rem .75rem; + user-select: none; + + transition: color .2s; + + img { + height: 2rem; + width: 2rem; + } + + &.selected { + border-color: var(--color-primary); + font-weight: bold; + color: var(--color-primary); + + img { + filter: invert(63%) sepia(54%) saturate(1053%) hue-rotate(359deg) brightness(100%) contrast(106%); + } + } + } + + img { + margin-right: 1rem; + user-select: none; + width: var(--font-size-l4); + } + } + + .votes { + display: grid; + grid-template-columns: 1fr 1fr; + } + + .rating, .pricing { + user-select: none; + + img { + cursor: pointer; + height: var(--font-size-l4); + margin: 0 .25rem; + width: var(--font-size-l4); + + &.active { + filter: invert(53%) sepia(30%) saturate(1977%) hue-rotate(155deg) brightness(88%) contrast(102%); + + &.selected { + filter: invert(63%) sepia(54%) saturate(1053%) hue-rotate(359deg) brightness(100%) contrast(106%); + } + } + } + } +} diff --git a/front/src/scss/modal/_startuphelp.scss b/front/src/scss/modal/_startuphelp.scss new file mode 100644 index 0000000..26568a6 --- /dev/null +++ b/front/src/scss/modal/_startuphelp.scss @@ -0,0 +1,89 @@ +.startup-help-modal { + .blue-precision { + color: #69E0D6; + font-weight: bold; + } + + .gold-range { + color: #FFD87D; + font-weight: bold; + } + + .green-spot { + color: #25A822; + font-weight: bold; + } + + .blue-shop { + color: #247DC9; + font-weight: bold; + } + + .red-bar { + color: #CA2A3D; + font-weight: bold; + } + + .helping-view-wrapper { + display: flex; + overflow: hidden; + position: relative; + + .previous, + .next { + position: fixed; + top: 50%; + transform: translateY(-50%); + + &.disabled { + pointer-events: none; + filter: grayscale(1) opacity(.3); + } + } + + .previous { + left: .5rem; + } + + .next { + right: .5rem; + } + + .page { + align-items: center; + display: flex; + flex-shrink: 0; + flex-direction: column; + height: 100%; + justify-content: space-around; + width: 100%; + + p img { + height: var(--font-size); + } + + .doc-image { + height: 100%; + max-height: 40rem; + } + } + } + + .page-counter { + display: flex; + justify-content: center; + + .page { + background-color: var(--color-txt); + border-radius: 50%; + height: var(--font-size); + margin: 0 .5rem; + width: var(--font-size); + transition: background-color .2s; + + &.active { + background-color: var(--color-primary); + } + } + } +} diff --git a/front/src/scss/modal/_userprofile.scss b/front/src/scss/modal/_userprofile.scss new file mode 100644 index 0000000..ae48e69 --- /dev/null +++ b/front/src/scss/modal/_userprofile.scss @@ -0,0 +1,88 @@ +.user-profile-modal { + .user-info { + border-bottom: solid 1px var(--color-txt-a25); + display: flex; + justify-content: center; + margin-bottom: var(--font-size-l4); + padding-bottom: var(--font-size-l4); + width: 100%; + + .drop-user-pp { + cursor: pointer; + height: 12rem; + margin-right: 2rem; + width: 12rem; + } + + img { + filter: drop-shadow(0 0 .5rem var(--color-txt-a5)); + height: 100%; + width: 100%; + } + } + + .item { + align-items: center; + display: flex; + justify-content: space-between; + margin: 1rem auto; + max-width: 30rem; + + img { + margin-right: 2rem; + height: 2rem; + } + + label { + margin-bottom: 0; + margin-right: 2rem; + } + + input { + margin: 0; + width: auto; + } + } + + .update-pp-wrapper { + border-radius: .5rem; + padding: 1rem; + + input { + cursor: pointer; + } + + .update-pp-error { + color: var(--color-primary); + font-style: italic; + margin-bottom: 0; + } + } + + .about-desc { + margin-top: 3rem; + } +} + +.update-pp-modal { + align-items: center; + background: var(--color-bg); + display: flex; + flex-direction: column; + height: calc(100% - 2rem); + justify-content: center; + position: absolute; + width: calc(100% - 2rem); + z-index: 50; /* Highest value, must be on top user profile modal */ + + .wip-pp-wrapper { + overflow: hidden; + position: relative; + + img { + max-height: 500px; + max-width: 500px; + width: 100%; + } + } +} \ No newline at end of file diff --git a/front/src/scss/utils/_base.scss b/front/src/scss/utils/_base.scss new file mode 100644 index 0000000..7ba18e5 --- /dev/null +++ b/front/src/scss/utils/_base.scss @@ -0,0 +1,168 @@ +:root { + --transition: .2s; + --transition-x2: calc(2 * var(--transition)); + + --color-primary: #ffa800; + --color-primary-a9: rgba(255, 168, 0, .9); + --color-primary-a75: rgba(255, 168, 0, .75); + --color-primary-a5: rgba(255, 168, 0, .5); + --color-primary-a25: rgba(255, 168, 0, .25); + --color-primary-a1: rgba(255, 168, 0, .1); + + --color-secondary: #0057ff; + --color-tertiary: #80abff; + + --font-size-s2: 1.05rem; + --font-size-s1: 1.20rem; + --font-size: 1.375rem; + --font-size-l1: 1.45rem; + --font-size-l2: 1.6rem; + --font-size-l3: 1.9rem; + --font-size-l4: 2.4rem; + --font-size-l5: 2.8rem; +} + +body.dark-theme { + --color-bg: #2E2E2E; + --color-bg-a9: rgba(46, 46, 46, .9); + --color-bg-a75: rgba(46, 46, 46, .75); + --color-bg-a5: rgba(46, 46, 46, .5); + --color-bg-a25: rgba(46, 46, 46, .25); + --color-bg-a1: rgba(46, 46, 46, .1); + + --color-txt: #E2E2E2; + --color-txt-a9: rgba(226, 226, 226, .9); + --color-txt-a75: rgba(226, 226, 226, .75); + --color-txt-a5: rgba(226, 226, 226, .5); + --color-txt-a25: rgba(226, 226, 226, .25); + --color-txt-a1: rgba(226, 226, 226, .1); + /* No overrides at all, dark theme is default one */ +} + +body.light-theme { + --color-bg: #E2E2E2; + --color-bg-a9: rgba(226, 226, 226, .9); + --color-bg-a75: rgba(226, 226, 226, .75); + --color-bg-a5: rgba(226, 226, 226, .5); + --color-bg-a25: rgba(226, 226, 226, .25); + --color-bg-a1: rgba(226, 226, 226, .1); + + --color-txt: #2E2E2E; + --color-txt-a9: rgba(46, 46, 46, .9); + --color-txt-a75: rgba(46, 46, 46, .75); + --color-txt-a5: rgba(46, 46, 46, .5); + --color-txt-a25: rgba(46, 46, 46, .25); + --color-txt-a1: rgba(46, 46, 46, .1); + /* I,vert svg icon that are white on white */ + .info-mark img, .edit img, + .rating img, .pricing img, + .types p img, .modifiers p img, + .item[data-type="label"] img, .item[data-type="circle"] img, + .item img, + .user-info img { + filter: invert(1); + } +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html, body { + font-size: 62.5%; + height: 100%; + overflow: hidden; + width: 100%; +} + +body { + background: var(--color-bg); + color: var(--color-txt); + font-family: sans-serif; + font-size: 1.4rem; + height: 100%; + position: relative; + width: 100%; +} + +h1 { + color: var(--color-txt); + font-size: var(--font-size-l5); + margin-bottom: var(--font-size); +} + +h2 { + font-size: var(--font-size-l4); +} + +a { + color: var(--color-primary); + font-size: var(--font-size); + margin-bottom: var(--font-size); +} + +p, label { + color: var(--color-txt); + font-size: var(--font-size); + margin-bottom: var(--font-size); + + &.error { + color: var(--color-primary); + font-weight: bold; + } +} + +label { + font-style: italic; +} + +input, textarea { + border: solid 1px var(--color-txt); + border-radius: .5rem; + background-color: transparent; + color: var(--color-txt); + display: block; + margin: .5rem auto; + margin-bottom: var(--font-size); + padding: .5rem; + width: 100%; + + transition: border .2s; +} + +input.error { + background-color: var(--color-primary-a5); +} + +button { + background-color: transparent; + border: solid 1px var(--color-txt); + border-radius: .5rem; + color: var(--color-txt); + cursor: pointer; + display: block; + margin: .5rem auto; + padding: .5rem; + width: 100%; + + transition: background-color .2s, color .2s; + + &.validate { + background-color: var(--color-primary-a75); + border: solid 1px var(--color-txt-a5); + color: var(--color-bg); + } + + &.cancel { + border: solid 1px var(--color-primary); + } + + &:active, + &:focus, + &:hover { + background-color: var(--color-primary); + color: var(--color-bg); + } +} diff --git a/front/src/scss/utils/_leaflet.scss b/front/src/scss/utils/_leaflet.scss new file mode 100644 index 0000000..d8b69f6 --- /dev/null +++ b/front/src/scss/utils/_leaflet.scss @@ -0,0 +1,105 @@ +/* Needs to override some leaflet styles to match BeerCrackerz interface */ +.leaflet-popup-content-wrapper { + background-color: var(--color-bg-a9); +} + +.leaflet-popup-tip-container .leaflet-popup-tip { + background-color: var(--color-bg-a9); +} + +.leaflet-container a.leaflet-popup-close-button { + font-size: 2.8rem; + right: .5rem; + top: .5rem; + + &:active, + &:focus, + &:hover { + color: var(--color-primary); + } +} + +.leaflet-control-layers.leaflet-control { + background: var(--color-bg); + background-clip: padding-box; + border: 2px solid var(--color-bg-a1); + border-radius: .5rem; + bottom: 156px; + box-shadow: 0 0 1rem var(--color-bg-a9); + position: absolute; + right: 0; + + a { + margin-bottom: 0; + } + + &.leaflet-control-layers-expanded { + padding: 2rem !important; + width: 18rem; + + label { + margin: 0; + + span { + align-items: center; + display: flex; + flex-direction: row-reverse; + justify-content: space-between; + + input { + width: auto; + } + + input, p { + margin: 0; + } + + p { + margin-right: 1rem; + } + } + } + } +} + +.leaflet-control-scale { + bottom: 20px; +} + +.leaflet-control-scale-line { + background-color: var(--color-bg-a75); + border: solid 2px var(--color-txt-a5); + color: var(--color-txt); + font-size: var(--font-size-s2); + text-shadow: none; +} + +.leaflet-control-attribution.leaflet-control { + background-color: var(--color-bg-a75); + color: var(--color-txt); + position: fixed; + left: 0; + bottom: 0; + + a { + color: var(--color-txt); + transition: color .2s; + + &:active, + &:focus, + &:hover { + color: var(--color-primary); + } + } +} + +.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive { + filter: drop-shadow(0 0 .25rem #424242); + transition: transform .2s, filter .2s; + + &:active, + &:focus, + &:hover { + filter: drop-shadow(0 0 .33rem #A8A8A8); + } +} diff --git a/front/src/scss/utils/_overlay.scss b/front/src/scss/utils/_overlay.scss new file mode 100644 index 0000000..36314d9 --- /dev/null +++ b/front/src/scss/utils/_overlay.scss @@ -0,0 +1,248 @@ +.overlay { + align-items: center; + background: var(--color-bg-a75); + display: none; + height: 100%; + justify-content: center; + position: absolute; + opacity: 0; + top: 0; + width: 100%; + z-index: 30; + + transition: opacity .3s; + + [class$="-modal"] { + background: var(--color-bg-a9); + border-radius: .5rem; + box-shadow: 0 0 1rem var(--color-bg-a5); + max-height: calc(100% - 2rem); + max-width: calc(100% - 2rem); + overflow-y: auto; + padding: 2rem; + position: relative; + text-align: center; + + h1 { + margin-bottom: 3rem; + } + + button { + margin-top: 3rem; + } + + .modal-close { + color: var(--color-txt); + cursor: pointer; + font-size: 2.6rem; + font-weight: bold; + line-height: var(--font-size-l3); /* Because the cross symbol doesn't fit font height */ + right: 1rem; + position: absolute; + top: 1rem; + + transition: color .2s; + + &:active, + &:focus, + &:hover { + color: var(--color-primary); + } + } + + .button-wrapper { + align-items: center; + display: flex; + justify-content: center; + + button { + margin-left: 1rem; + margin-right: 1rem; + } + } + } + + @import '../modal/mark'; + @import '../modal/hideshow'; + @import '../modal/userprofile'; + @import '../modal/startuphelp'; +} + +.debug-container { + background-color: var(--color-bg-a9); + border-radius: .5rem; + left: 1rem; + padding: 1rem; + position: absolute; + top: 1rem; + z-index: 20; + + h1 { + font-size: var(--font-size-l3); + margin-bottom: var(--font-size); + text-align: center; + } + + p { + display: flex; + justify-content: space-between; + margin-bottom: .2rem; + + b { + margin-right: 2rem; + } + } + + button { + margin-top: var(--font-size); + } +} + +.popup { + width: 30rem; + + h1, h2, p { + color: var(--color-txt); + margin: 0; + text-align: center; + word-wrap: break-word; + } + + h1 { + font-size: 2rem; + margin-bottom: var(--font-size); + } + + .types, .modifiers { + align-items: center; + display: flex; + flex-wrap: wrap; + justify-content: center; + margin: .5rem auto var(--font-size); + + p { + align-items: center; + border: solid 1px var(--color-txt); + border-radius: .5rem; + color: var(--color-txt); + cursor: pointer; + display: flex; + flex-direction: row; + justify-content: center; + margin: .5rem; + padding: .25rem .75rem; + user-select: none; + + transition: color .2s; + + img { + height: 2rem; + width: 2rem; + } + + &.selected { + border-color: var(--color-primary); + color: var(--color-primary); + + img { + filter: invert(63%) sepia(54%) saturate(1053%) hue-rotate(359deg) brightness(100%) contrast(106%); + } + } + } + + img { + margin-right: .5rem; + user-select: none; + width: var(--font-size-l4); + } + } + + .modifiers { + margin: var(--font-size) auto .5rem; + } + + .rating { + align-items: center; + display: flex; + justify-content: center; + width: 100%; + + img { + height: var(--font-size); + + &.active { + filter: invert(63%) sepia(54%) saturate(1053%) hue-rotate(359deg) brightness(100%) contrast(106%); + } + } + + p { + font-style: inherit; + margin-left: .5rem; + } + } + + p { + font-style: italic; + } + + h2 { + font-size: var(--font-size-l1); + font-weight: inherit; + margin-top: var(--font-size); + } + + footer { + align-items: center; + display: flex; + position: relative; + justify-content: space-between; + margin-top: var(--font-size); + + img { + cursor: pointer; + height: 2rem; + margin-left: .5rem; + } + } +} + +.cluster-icon-wrapper { + position: relative; + + .cluster-icon { + width: auto; + height: 50px; + line-height: 50px; + margin-left: -21px; + margin-top: -13px; + filter: drop-shadow(0 0 .25rem var(--color-bg)); + transition: transform .2s, filter .2s; + + &:active, + &:focus, + &:hover { + filter: drop-shadow(0 0 .33rem var(--color-txt)); + transform: scale(1.066); + } + } + + .cluster-label { + background-color: var(--color-bg-a9); + border: solid var(--color-bg) 1px; + border-radius: .75rem; + box-shadow: 0 0 1rem var(--color-bg); + color: var(--color-txt); + font-size: 1.1rem; + font-weight: bold; + left: 15px; + padding: .1rem .4rem; + position: absolute; + top: -15px; + + &:active, + &:focus, + &:hover { + color: var(--color-primary); + } + } +} diff --git a/front/src/scss/utils/_responsive.scss b/front/src/scss/utils/_responsive.scss new file mode 100644 index 0000000..6f645eb --- /dev/null +++ b/front/src/scss/utils/_responsive.scss @@ -0,0 +1,43 @@ +@media (min-width: 720px) { + .overlay [class$=-modal] { + padding: 3rem 4rem; + } +} + +@media (min-width: 1080px) { + .overlay [class$="-modal"] { + max-width: 50%; + } +} +/* Startup help modal rules */ +@media (max-height: 750px) { + .overlay .startup-help-modal .helping-view-wrapper { + .page .doc-image { + max-height: 28rem; + } + } +} + +@media (max-height: 600px) { + .overlay .startup-help-modal .helping-view-wrapper { + .page .doc-image { + max-height: 18rem; + } + } +} + +@media (max-height: 500px) { + .overlay .startup-help-modal .helping-view-wrapper { + .page .doc-image { + max-height: 11rem; + } + } +} + +@media (max-height: 400px) { + .overlay .startup-help-modal .helping-view-wrapper { + .page .doc-image { + max-height: 8rem; + } + } +} diff --git a/front/test/karma.config.js b/front/test/karma.config.js new file mode 100644 index 0000000..adc4cfe --- /dev/null +++ b/front/test/karma.config.js @@ -0,0 +1,61 @@ +const webpack = require('webpack'); +const loaders = require('../webpack/loaders'); +const plugins = require('../webpack/plugins'); + +module.exports = config => { + config.set({ + basePath: '../', + singleRun: !config.dev, // Keep browser open in dev mode + browsers: ['Firefox'], + frameworks: ['jasmine'], + client: { + jasmine: { + clearContext: false, + random: !config.dev // Randomly run test when not developping them + } + }, + files: [ + 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css', + 'https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css', + 'https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.css', + 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', + 'https://unpkg.com/leaflet.markercluster@1.4.1/dist/leaflet.markercluster.js', + 'test/testContext.js', + 'test/testStyle.css', + 'test/templates/index.html', + '../static/dist/*.css' + ], + reporters: ['kjhtml', 'progress'], + preprocessors: { + 'test/testContext.js': ['webpack'], + '**/*.html': ['html2js'] + }, + babelPreprocessor: { + options: { + presets: ['env'], + sourceMap: false + } + }, + webpack: { + devtool: false, + module: { + rules: [ + loaders.JSLoader, + loaders.CSSLoader + ] + }, + plugins: [ + new webpack.ProgressPlugin(), + plugins.CleanWebpackPlugin, + plugins.ESLintPlugin, + plugins.StyleLintPlugin, + plugins.MiniCssExtractPlugin + ], + watch: true, + mode: 'development' + }, + webpackServer: { + noInfo: true + } + }); +}; diff --git a/front/test/spec/ui.ModalFactory.spec.js b/front/test/spec/ui.ModalFactory.spec.js new file mode 100644 index 0000000..bd220f7 --- /dev/null +++ b/front/test/spec/ui.ModalFactory.spec.js @@ -0,0 +1,55 @@ +import ModalFactory from '../../src/js/ui/ModalFactory.js'; + + +let backup = null; // console.error backup +describe('ModalFactory unit test :', () => { + +/* + beforeAll(() => { + const container = document.createElement('DIV'); + container.className = 'container'; + document.body.appendChild(container); + + const rawPage = window.__html__['test/templates/index.html']; + const html = document.createRange().createContextualFragment(rawPage) + container.appendChild(html); + // Add csrf token to make Kom work properly + document.cookie = 'csrftoken=FakeCSRF; path=/'; + // Add bundled CSS + const link = document.createElement('script'); + link.type = 'stylesheet'; + link.src = 'base/static/dist/BeerCrackerzBundle.css'; + document.head.appendChild(link); + }); +*/ + + beforeEach(() => { + // Instantiating modal will raise errors in console but we're not tesing modal themselves here + backup = console.error; + console.error = () => {}; + window.BeerCrackerz = { + kom: { getTemplate: () => { return Promise.resolve('Test'); } } + }; + }); + + + afterEach(() => { + console.error = backup; + window.BeerCrackerz = null; + }); + + + it('Public static method build', done => { + expect(ModalFactory.build()).toEqual(null); + expect(ModalFactory.build('NotModal')).toEqual(null); + expect(JSON.stringify(ModalFactory.build('AddMark', { type: 'spot' }))).toEqual('{"_type":"addspot","_url":"/modal/addspot","_rootElement":null,"_modalOverlay":null,"_closeButton":null,"_evtIds":[],"_opts":{"type":"spot"},"_action":"add","_name":"","_description":"","_rating":null,"_pricing":null}'); + expect(JSON.stringify(ModalFactory.build('DeleteMark'))).toEqual('{"_type":"deletemark","_url":"/modal/deletemark","_rootElement":null,"_modalOverlay":null,"_closeButton":null,"_evtIds":[],"_opts":{},"_footerCancelButton":null,"_footerSubmitButton":null}'); + expect(JSON.stringify(ModalFactory.build('EditMark', { type: 'shop' }))).toEqual('{"_type":"editshop","_url":"/modal/editshop","_rootElement":null,"_modalOverlay":null,"_closeButton":null,"_evtIds":[],"_opts":{"type":"shop"},"_action":"edit","_name":"","_description":"","_rating":null,"_pricing":null}'); + expect(JSON.stringify(ModalFactory.build('User'))).toEqual('{"_type":"user","_url":"/modal/user","_rootElement":null,"_modalOverlay":null,"_closeButton":null,"_evtIds":[],"_opts":{},"_footerCancelButton":null,"_footerSubmitButton":null}'); + expect(JSON.stringify(ModalFactory.build('UpdateProfilePicture'))).toEqual('{"_type":"updatepp","_url":"/modal/updatepp","_rootElement":null,"_modalOverlay":null,"_closeButton":null,"_evtIds":[],"_opts":{},"_footerCancelButton":null,"_footerSubmitButton":null}'); + expect(JSON.stringify(ModalFactory.build('HideShow'))).toEqual('{"_type":"hideshow","_url":"/modal/hideshow","_rootElement":null,"_modalOverlay":null,"_closeButton":null,"_evtIds":[],"_opts":{},"_footerCancelButton":null,"_footerSubmitButton":null}'); + done(); + }); + + +}); diff --git a/front/test/spec/ui.VisuHelper.spec.js b/front/test/spec/ui.VisuHelper.spec.js new file mode 100644 index 0000000..6cc4c09 --- /dev/null +++ b/front/test/spec/ui.VisuHelper.spec.js @@ -0,0 +1,68 @@ +import VisuHelper from '../../src/js/ui/VisuHelper.js'; +import nls from '../../../static/nls/en.json'; + + +describe('VisuHelper unit test :', () => { + + + beforeEach(() => { + window.VERSION = '42.0.0'; + window.BeerCrackerz = { + debugElement: null, + nls: { + debug: key => { + return nls.debug[key]; + } + } + }; + }); + + + afterEach(() => { + window.VERSION = null; + window.BeerCrackerz = null; + }); + + + it('Public static method initDebugUI', done => { + expect(typeof VisuHelper.initDebugUI).toEqual('function'); + // Standard debug UI + const element = VisuHelper.initDebugUI(); + expect(element.classList.contains('debug-container')).toEqual(true); + expect(element.children.length).toEqual(10); + expect(element.children[0].innerHTML).toEqual(`BeerCrackerz v${window.VERSION}`); + expect(element.children[1].classList.contains('debug-user-lat')).toEqual(true); + expect(element.children[1].innerHTML).toEqual(`${nls.debug.lat} -`); + expect(element.children[2].classList.contains('debug-user-lng')).toEqual(true); + expect(element.children[2].innerHTML).toEqual(`${nls.debug.lng} -`); + expect(element.children[3].classList.contains('debug-updates-amount')).toEqual(true); + expect(element.children[3].innerHTML).toEqual(`${nls.debug.updates} 0`); + expect(element.children[4].classList.contains('debug-user-accuracy')).toEqual(true); + expect(element.children[4].innerHTML).toEqual(`${nls.debug.accuracy} -`); + expect(element.children[5].classList.contains('debug-high-accuracy')).toEqual(true); + expect(element.children[5].innerHTML).toEqual(`${nls.debug.highAccuracy} -`); + expect(element.children[6].classList.contains('debug-pos-max-age')).toEqual(true); + expect(element.children[6].innerHTML).toEqual(`${nls.debug.posAge} -`); + expect(element.children[7].classList.contains('debug-pos-timeout')).toEqual(true); + expect(element.children[7].innerHTML).toEqual(`${nls.debug.posTimeout} -`); + expect(element.children[8].classList.contains('debug-zoom-level')).toEqual(true); + expect(element.children[8].innerHTML).toEqual(`${nls.debug.zoom} -`); + expect(element.children[9].classList.contains('debug-marks-amount')).toEqual(true); + expect(element.children[9].innerHTML).toEqual(`${nls.debug.marks} -`); + // End init debug UI tests + done(); + }); + + + it('Public static method addDebugUI', done => { + expect(typeof VisuHelper.addDebugUI).toEqual('function'); + window.BeerCrackerz.debugElement = VisuHelper.initDebugUI(); + // Standard debug UI + VisuHelper.addDebugUI(); + expect(window.BeerCrackerz.debugElement.parentNode).toEqual(document.body); + // End init debug UI tests + done(); + }); + + +}); diff --git a/front/test/spec/utils.CustomEvents.spec.js b/front/test/spec/utils.CustomEvents.spec.js new file mode 100644 index 0000000..d11f97f --- /dev/null +++ b/front/test/spec/utils.CustomEvents.spec.js @@ -0,0 +1,311 @@ +import CustomEvents from '../../src/js/utils/CustomEvents.js'; + + +// Working component, re-instantiated before each test +let AppEvents = null; + + +describe('CustomEvents unit test :', () => { + + + beforeEach(() => { + AppEvents = new CustomEvents(); + }); + + + it('Instantiation', done => { + // Component existence + expect(AppEvents).not.toEqual(undefined); + expect(AppEvents).not.toEqual(null); + // Component proper construction + expect(AppEvents._debug).toEqual(false); + expect(typeof AppEvents._idIncrementor).toEqual('number'); + expect(AppEvents._regularEvents.length).toEqual(0); + expect(Object.keys(AppEvents._customEvents).length).toEqual(0); + expect(AppEvents.version).toEqual('1.2.1'); // Check version number + // Re-instantiate AppEvents in debug + AppEvents = new CustomEvents(true); + // Component proper construction + expect(AppEvents._debug).toEqual(true); + // Send string instead of boolean + AppEvents = new CustomEvents('Not a string'); + // Component proper construction + expect(AppEvents._debug).toEqual(false); + done(); + }); + + + it('Destruction', done => { + AppEvents.destroy(); + // Check proper object destruction + expect(JSON.stringify(AppEvents)).toEqual('{}'); + done(); + }); + + + it('Public method addEvent', done => { + const testDiv = document.createElement('DIV'); + // Mandatory arguments + // Send number instead of string for eventName + expect(AppEvents.addEvent(42, testDiv, () => {})).toEqual(false); + // Send string instead of object for element + expect(AppEvents.addEvent('click', 'Not a string', () => {})).toEqual(false); + // Send string instead of function for callback + expect(AppEvents.addEvent('click', testDiv, 'Not a string')).toEqual(false); + // Optional arguments + // Send string instead of object for scope + expect(AppEvents.addEvent('click', testDiv, () => {}, 'Not a string')).toEqual(false); + let called = 0; + const cb = event => { + // Test object construction + expect(AppEvents._regularEvents.length).toEqual(1); // We only have one event listener + expect(AppEvents._regularEvents[0].id).toEqual(AppEvents._idIncrementor - 1); + expect(AppEvents._regularEvents[0].element).toEqual(testDiv); + expect(AppEvents._regularEvents[0].eventName).toEqual('click'); + expect(AppEvents._regularEvents[0].scope).toEqual(testDiv); // Default scope is element + expect(typeof AppEvents._regularEvents[0].callback).toEqual('function'); + expect(AppEvents._regularEvents[0].options).toEqual(false); + // Test received event, must be 'click' + expect(typeof event).toEqual('object'); + expect(event.type).toEqual('click'); + called++; + }; + // Proper subscription and return value + const evtId = AppEvents.addEvent('click', testDiv, cb); + expect(typeof evtId).toEqual('number'); + expect(evtId).toEqual(AppEvents._idIncrementor - 1); + // Trigger that regular event + testDiv.click(); + expect(called).toEqual(1); + testDiv.click(); + expect(called).toEqual(2); + // Missing argument test + expect(AppEvents.addEvent()).toEqual(false); + expect(AppEvents.addEvent('click', null, null)).toEqual(false); + expect(AppEvents.addEvent('click', testDiv, null)).toEqual(false); + expect(AppEvents.addEvent(null, testDiv, null)).toEqual(false); + expect(AppEvents.addEvent(null, testDiv, () => {})).toEqual(false); + expect(AppEvents.addEvent(null, null, () => {})).toEqual(false); + done(); + }); + + + it('Public method removeEvent', done => { + // Mandatory arguments + // Send number instead of string for eventId + expect(AppEvents.removeEvent(42)).toEqual(false); + // Event callback + let called = 0; + const cb = () => { called++; }; + const testDiv = document.createElement('DIV'); + const evtId = AppEvents.addEvent('click', testDiv, cb); + // First click + testDiv.click(); + expect(called).toEqual(1); + expect(AppEvents._regularEvents.length).toEqual(1); + // Remove event + expect(AppEvents.removeEvent(evtId)).toEqual(true); + // Second click + testDiv.click(); + expect(called).toEqual(1); + expect(AppEvents._regularEvents.length).toEqual(0); + // Remove an already removed event + expect(AppEvents.removeEvent(evtId)).toEqual(false); + // Missing argument test + expect(AppEvents.removeEvent()).toEqual(false); + done(); + }); + + + it('Public method removeAllEvents', done => { + // Prepare test with 2 events and a test div + const testDiv = document.createElement('DIV'); + AppEvents.addEvent('mousedown', testDiv, () => {}); + AppEvents.addEvent('mouseup', testDiv, () => {}); + expect(AppEvents._regularEvents.length).toEqual(2); + // Call for removeAllEvents + expect(AppEvents.removeAllEvents()).toEqual(true); + expect(AppEvents._regularEvents.length).toEqual(0); + // No event can be removed at this point, returning a false status + expect(AppEvents.removeAllEvents()).toEqual(false); + done(); + }); + + + it('Public method subscribe', done => { + // Mandatory arguments + // Send number instead of string for eventName + expect(AppEvents.subscribe(42, () => {})).toEqual(false); + // Send string instead of string for callback + expect(AppEvents.subscribe('TestEvent', 'Not a string')).toEqual(false); + // Optional arguments + // Send string instead of boolean for oneShot + expect(AppEvents.subscribe('TestEvent', () => {}, 'Not a string')).toEqual(false); + let called = 0; + const evtId = AppEvents.subscribe('TestEvent', () => { + expect(Object.keys(AppEvents._customEvents['TestEvent']).length).toEqual(1); + expect(AppEvents._customEvents['TestEvent'][0].id).toEqual(AppEvents._idIncrementor - 1); + expect(AppEvents._customEvents['TestEvent'][0].name).toEqual('TestEvent'); + expect(AppEvents._customEvents['TestEvent'][0].os).toEqual(false); + expect(typeof AppEvents._customEvents['TestEvent'][0].callback).toEqual('function'); + called++; + }); + expect(typeof evtId).toEqual('number'); + expect(evtId).toEqual(AppEvents._idIncrementor - 1); + // All subscribe expects will be handle in publish call + AppEvents.publish('TestEvent'); + // Check proper Events state + expect(Object.keys(AppEvents._customEvents).length).toEqual(1); + expect(AppEvents._customEvents['TestEvent']).not.toEqual(undefined); + expect(AppEvents._customEvents['TestEvent']).not.toEqual(null); + expect(called).toEqual(1); + // Call again for publish to check incrementation + AppEvents.publish('TestEvent'); + expect(called).toEqual(2); + // Missing parameters for subscribe + expect(AppEvents.subscribe()).toEqual(false); + expect(AppEvents.subscribe('TestEvt', null)).toEqual(false); + expect(AppEvents.subscribe(null, () => {})).toEqual(false); + // Testing with one shot + AppEvents.unsubscribe(evtId); + called = 0; + AppEvents.subscribe('TestEventOS', () => { + expect(Object.keys(AppEvents._customEvents['TestEventOS']).length).toEqual(1); // We only have one subscription on event + expect(AppEvents._customEvents['TestEventOS'][0].name).toEqual('TestEventOS'); + expect(AppEvents._customEvents['TestEventOS'][0].os).toEqual(true); // One shot event + expect(typeof AppEvents._customEvents['TestEventOS'][0].callback).toEqual('function'); + called++; + }, true); // Send true for one shot + // All subscribe expects will be handle in publish call + AppEvents.publish('TestEventOS'); + // Check proper Events state + expect(Object.keys(AppEvents._customEvents).length).toEqual(0); + expect(called).toEqual(1); + // All subscribe expects will be handle in publish call, we send here data + AppEvents.publish('TestEventOS'); + expect(called).toEqual(1); + done(); + }); + + + it('Public method unsubscribe', done => { + // Mandatory arguments + // Send string instead of number for eventName + expect(AppEvents.unsubscribe('Not a string')).toEqual(false); + const subsId = AppEvents.subscribe('TestEvent', () => {}); + expect(Object.keys(AppEvents._customEvents).length).toEqual(1); + // All subscribe expects will be handle in publish call, we send here data + expect(AppEvents.unsubscribe(subsId)).toEqual(true); + expect(Object.keys(AppEvents._customEvents).length).toEqual(0); + expect(AppEvents.unsubscribe(subsId)).toEqual(false); + // Missing parameters for subscribe + expect(AppEvents.unsubscribe()).toEqual(false); + done(); + }); + + + it('Public method unsubscribeAllFor', done => { + // Mandatory arguments + // Send number instead of string for eventName + expect(AppEvents.unsubscribeAllFor(42)).toEqual(false); + AppEvents.subscribe('TestEvent', () => {}); + AppEvents.subscribe('TestEvent', () => {}); + expect(Object.keys(AppEvents._customEvents).length).toEqual(1); + // All subscribe expects will be handle in publish call, we send here data + expect(AppEvents.unsubscribeAllFor('TestEvent')).toEqual(true); + expect(Object.keys(AppEvents._customEvents).length).toEqual(0); + expect(AppEvents.unsubscribeAllFor('TestEvent')).toEqual(false); + // Missing parameters for subscribe + expect(AppEvents.unsubscribeAllFor()).toEqual(false); + done(); + }); + + + it('Public method publish', done => { + // No specific data + AppEvents.subscribe('TestEvent', data => { + expect(data).toEqual(null); // Default value for empty data must be undefined + }, true); + // All subscribe expects will be handle in publish call + expect(AppEvents.publish('TestEvent')).toEqual(true); + // With data attached + AppEvents.subscribe('TestEvent', data => { + expect(JSON.stringify(data)).toEqual(`{"passedData":[0,"1"]}`); + }, true); + // Proper method call + expect(AppEvents.publish('TestEvent', { passedData: [0, '1'] })).toEqual(true); + // No registered event test + expect(AppEvents.publish('NotTestEvent')).toEqual(false); + // Missing mandatory arguments + expect(AppEvents.publish()).toEqual(false); + // Wrong arguments + // Mandatory arguments + // Send number instead of string for eventName + expect(AppEvents.publish(42)).toEqual(false); + // Send number instead of object for data + expect(AppEvents.publish('TestEvent', 42)).toEqual(false); + done(); + }); + + + it('Private method _clearRegularEvent', done => { + const testDiv = document.createElement('DIV'); + AppEvents.addEvent('click', testDiv, () => {}); + expect(AppEvents._regularEvents.length).toEqual(1); + // Mandatory arguments + // Send function instead of number for index + expect(AppEvents._clearRegularEvent(() => {})).toEqual(false); + // Clear without any index won't do anything + expect(AppEvents._clearRegularEvent()).toEqual(false); + expect(AppEvents._regularEvents.length).toEqual(1); + // Now clear with first item index in stored event + expect(AppEvents._clearRegularEvent(0)).toEqual(true); + expect(AppEvents._regularEvents.length).toEqual(0); + done(); + }); + + + it('Scope binding validation', done => { + let called = 0; + const testDiv = document.createElement('DIV'); + class TestClass { + constructor(AppEvents) { + this.attribute = 'attr-value'; + this.evtId = AppEvents.addEvent('click', testDiv, this.cbWithScope, this); + this.scopelessEvtId = AppEvents.addEvent('click', testDiv, this.cbWithoutScope); + /* Testing part */ + expect(AppEvents._regularEvents.length).toEqual(2); + testDiv.click(); + expect(called).toEqual(2); + AppEvents.removeEvent(this.evtId); + expect(AppEvents._regularEvents.length).toEqual(1); + testDiv.click(); + expect(called).toEqual(3); + AppEvents.removeEvent(this.scopelessEvtId); + expect(AppEvents._regularEvents.length).toEqual(0); + testDiv.click(); + expect(called).toEqual(3); + done(); + } + + cbWithScope() { + // Check that scope has been preserved in class callback + expect(this).not.toEqual(testDiv); + expect(this.attribute).toEqual('attr-value'); + expect(typeof this.evtId).toEqual('number'); + called++; + } + + + cbWithoutScope() { + // Check that scope is element one + expect(this).toEqual(testDiv); + called++; + } + } + // In real use case, AppEvents would be imported as component in class definition file + new TestClass(AppEvents); + }); + + +}); diff --git a/front/test/spec/utils.Utils.spec.js b/front/test/spec/utils.Utils.spec.js new file mode 100644 index 0000000..3bd18b3 --- /dev/null +++ b/front/test/spec/utils.Utils.spec.js @@ -0,0 +1,283 @@ +import Utils from '../../src/js/utils/Utils.js'; + + +describe('Utils unit test :', () => { + + + it('Public static method stripDom', done => { + expect(typeof Utils.stripDom).toEqual('function'); + // Invalid argument args (empty or mistyped) + expect(Utils.stripDom()).toEqual(''); + expect(Utils.stripDom(true)).toEqual(''); + expect(Utils.stripDom([ true, false ])).toEqual(''); + expect(Utils.stripDom({ true: true })).toEqual(''); + expect(Utils.stripDom(() => {})).toEqual(''); + // Standard striping behavior args + expect(Utils.stripDom(42)).toEqual('42'); + expect(Utils.stripDom('html')).toEqual('html'); + expect(Utils.stripDom('

        html

        ')).toEqual('html'); + expect(Utils.stripDom('')).toEqual(''); + expect(Utils.stripDom('<>')).toEqual('<>'); + // End stripDom tests + done(); + }); + + + it('Public static method replaceString', done => { + expect(typeof Utils.replaceString).toEqual('function'); + // Local element for testing + const testElement = document.createElement('P'); + testElement.innerHTML = 'test string'; + // Invalid first argument args (empty or mistyped) + expect(Utils.replaceString()).toEqual(false); + expect(Utils.replaceString(true)).toEqual(false); + expect(Utils.replaceString(42)).toEqual(false); + expect(Utils.replaceString('element')).toEqual(false); + expect(Utils.replaceString([ true, false ])).toEqual(false); + expect(Utils.replaceString({ true: true })).toEqual(false); + expect(Utils.replaceString(() => {})).toEqual(false); + // Invalid second argument args (empty or mistyped) + expect(Utils.replaceString(testElement)).toEqual(false); + expect(Utils.replaceString(testElement, null, 'value')).toEqual(false); + expect(Utils.replaceString(testElement, true, 'value')).toEqual(false); + expect(Utils.replaceString(testElement, 42, 'value')).toEqual(false); + expect(Utils.replaceString(testElement, [ true, false ], 'value')).toEqual(false); + expect(Utils.replaceString(testElement, { true: true }, 'value')).toEqual(false); + expect(Utils.replaceString(testElement, () => {}, 'value')).toEqual(false); + // Invalid thrid argument args (empty or mistyped) + expect(Utils.replaceString(testElement, 'string')).toEqual(false); + expect(Utils.replaceString(testElement, 'string', null)).toEqual(false); + expect(Utils.replaceString(testElement, 'string', true)).toEqual(false); + expect(Utils.replaceString(testElement, 'string', 42)).toEqual(false); + expect(Utils.replaceString(testElement, 'string', [ true, false ])).toEqual(false); + expect(Utils.replaceString(testElement, 'string', { true: true })).toEqual(false); + expect(Utils.replaceString(testElement, 'string', () => {})).toEqual(false); + // Standard replace behavior + expect(testElement.innerHTML).toEqual('test string'); + expect(Utils.replaceString(testElement, 'string', 'replaced string')).toEqual(true); + expect(testElement.innerHTML).toEqual('test replaced string'); + expect(Utils.replaceString(testElement, 'gnirts', 'replaced string')).toEqual(true); + expect(testElement.innerHTML).toEqual('test replaced string'); + // End replaceString tests + done(); + }); + + + it('Public static method getDistanceBetweenCoords', done => { + expect(typeof Utils.getDistanceBetweenCoords).toEqual('function'); + // Local coordinates for testing + let from = [21, -15]; + let to = [-18, 32]; + // Invalid first argument args (empty or mistyped) + expect(Utils.getDistanceBetweenCoords()).toEqual(-1); + expect(Utils.getDistanceBetweenCoords(true)).toEqual(-1); + expect(Utils.getDistanceBetweenCoords([ true, false ])).toEqual(-1); + expect(Utils.getDistanceBetweenCoords({ true: true })).toEqual(-1); + expect(Utils.getDistanceBetweenCoords(() => {})).toEqual(-1); + // Invalid second argument args (empty or mistyped) + expect(Utils.getDistanceBetweenCoords(from)).toEqual(-1); + expect(Utils.getDistanceBetweenCoords(from, true)).toEqual(-1); + expect(Utils.getDistanceBetweenCoords(from, [ true, false ])).toEqual(-1); + expect(Utils.getDistanceBetweenCoords(from, { true: true })).toEqual(-1); + expect(Utils.getDistanceBetweenCoords(from, () => {})).toEqual(-1); + // Standard distance behavior + expect(Utils.getDistanceBetweenCoords(from, to)).toEqual(6709911.901531838); + from = [25, 0]; + to = [26, 0]; + expect(Utils.getDistanceBetweenCoords(from, to)).toEqual(111194.92664455889); // Testing on lat delta only + from = [0, 43]; + to = [0, 42]; + expect(Utils.getDistanceBetweenCoords(from, to)).toEqual(111194.92664455819); // Testing on lng delta only + // End getDistanceBetweenCoords tests + done(); + }); + + + it('Public static method precisionRound', done => { + expect(typeof Utils.precisionRound).toEqual('function'); + // Invalid first argument args (empty or mistyped) + expect(Utils.precisionRound()).toEqual(-1); + expect(Utils.precisionRound(true)).toEqual(-1); + expect(Utils.precisionRound('value')).toEqual(-1); + expect(Utils.precisionRound([ true, false ])).toEqual(-1); + expect(Utils.precisionRound({ true: true })).toEqual(-1); + expect(Utils.precisionRound(() => {})).toEqual(-1); + // Invalid second argument args (empty or mistyped) + expect(Utils.precisionRound(1.123456789)).toEqual(-1); + expect(Utils.precisionRound(1.123456789, true)).toEqual(-1); + expect(Utils.precisionRound(1.123456789, 'precision')).toEqual(-1); + expect(Utils.precisionRound(1.123456789, [ true, false ])).toEqual(-1); + expect(Utils.precisionRound(1.123456789, { true: true })).toEqual(-1); + expect(Utils.precisionRound(1.123456789, () => {})).toEqual(-1); + // Standard rounding behavior + expect(Utils.precisionRound(0, 0)).toEqual(0); // Zero testing + expect(Utils.precisionRound(0, 2)).toEqual(0); + expect(Utils.precisionRound(2.4, 0)).toEqual(2); + expect(Utils.precisionRound(2.5, 0)).toEqual(3); + expect(Utils.precisionRound(-4.987654321, 0)).toEqual(-5); // Rounding > .5 + expect(Utils.precisionRound(-4.987654321, 1)).toEqual(-5); + expect(Utils.precisionRound(-4.987654321, 2)).toEqual(-4.99); + expect(Utils.precisionRound(-4.987654321, 3)).toEqual(-4.988); + expect(Utils.precisionRound(-4.987654321, 4)).toEqual(-4.9877); + expect(Utils.precisionRound(4.987654321, 0)).toEqual(5); + expect(Utils.precisionRound(4.987654321, 1)).toEqual(5); + expect(Utils.precisionRound(4.987654321, 2)).toEqual(4.99); + expect(Utils.precisionRound(4.987654321, 3)).toEqual(4.988); + expect(Utils.precisionRound(4.987654321, 4)).toEqual(4.9877); + expect(Utils.precisionRound(4.123456789, 0)).toEqual(4); // Rounding < .5 + expect(Utils.precisionRound(4.123456789, 1)).toEqual(4.1); + expect(Utils.precisionRound(4.123456789, 2)).toEqual(4.12); + expect(Utils.precisionRound(4.123456789, 3)).toEqual(4.123); + expect(Utils.precisionRound(4.123456789, 4)).toEqual(4.1235); + expect(Utils.precisionRound(-4.123456789, 0)).toEqual(-4); + expect(Utils.precisionRound(-4.123456789, 1)).toEqual(-4.1); + expect(Utils.precisionRound(-4.123456789, 2)).toEqual(-4.12); + expect(Utils.precisionRound(-4.123456789, 3)).toEqual(-4.123); + expect(Utils.precisionRound(-4.123456789, 4)).toEqual(-4.1235); + expect(Utils.precisionRound(1 / 500, 10)).toEqual(0.002); // Fractions and other + expect(Utils.precisionRound(1 / -500, 10)).toEqual(-0.002); + expect(Utils.precisionRound(Math.PI, 10)).toEqual(3.1415926536); + // End rounding tests + done(); + }); + + + it('Public static method setDefaultPreferences', done => { + expect(typeof Utils.setDefaultPreferences).toEqual('function'); + // Local element for testing + const defaultPreferences = { + 'poi-show-spot': true, + 'poi-show-shop': true, + 'poi-show-bar': true, + 'map-plan-layer': 'Plan OSM', + 'selected-lang': 'en', + 'app-debug': false, + 'map-high-accuracy': false, + 'map-center-on-user': false, + 'dark-theme': true, + }; + let called = 0; + spyOn(Utils, 'setPreference').and.callFake((pref, value) => { + ++called; + expect(defaultPreferences[pref]).toEqual(value); + // End default pref tests + if (called === 11) { + done(); + } + }); + // Force getPref to alway return null so we can check default values are properly set + spyOn(Utils, 'getPreference').and.callFake(() => { + return null; + }); + // Call for default preferences setter + Utils.setDefaultPreferences(); + }); + + + it('Public static method formatMarker', done => { + expect(typeof Utils.formatMarker).toEqual('function'); + // Invalid argument args (empty) + expect(Utils.formatMarker()).toEqual(null); + expect(Utils.formatMarker(true)).toEqual(null); + expect(Utils.formatMarker(0)).toEqual(null); + expect(Utils.formatMarker('mark')).toEqual(null); + expect(Utils.formatMarker([ true, false ])).toEqual(null); + expect(Utils.formatMarker({ true: true })).toEqual(null); + expect(Utils.formatMarker(() => {})).toEqual(null); + // Invalid argument args (name/types) + expect(Utils.formatMarker({ name: true, types: ['a', 'b'] })).toEqual(null); + expect(Utils.formatMarker({ name: 42, types: ['a', 'b'] })).toEqual(null); + expect(Utils.formatMarker({ name: [ true, false ], types: ['a', 'b'] })).toEqual(null); + expect(Utils.formatMarker({ name: { true: true }, types: ['a', 'b'] })).toEqual(null); + expect(Utils.formatMarker({ name: () => {}, types: ['a', 'b'] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: true })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: 'types' })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: () => {} })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: [ true, false ] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'] })).toEqual(null); // Good, but not enough objects (lat/lng missing) + // Invalid argument args (lat/lng) + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lng: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: true, lng: 42 })).toEqual(null); // Sending proper lng, testing lat + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: '42', lng: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: [ true, false ], lng: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: { false: true }, lng: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: () => {}, lng: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: true })).toEqual(null); // Sending proper lat, testing lng + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: '42' })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: [ true, false ] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: () => {} })).toEqual(null); + // Invalid argument args (description) + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: true })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: [ false ] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: () => {} })).toEqual(null); + // Invalid argument args (modifiers) + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: true })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: 42 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: 'modifiers' })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: () => {} })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: [true] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: [42] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: [[ false, true ]] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: [{ false: true }] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: [() => {}] })).toEqual(null); + // Invalid argument args (rate) + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: true })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 'rate' })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: [ false, true ] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: () => {} })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: -1 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 5 })).toEqual(null); + // Invalid argument args (price) + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: true })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: 'price' })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: [ false, true ] })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: { false: true } })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: () => {} })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: -1 })).toEqual(null); + expect(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'desc', modifiers: ['c'], rate: 2, price: 3 })).toEqual(null); + // Standard formatting behavior + expect(JSON.stringify(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42 }))).toEqual('{"name":"Name","types":["a","b"],"lat":42,"lng":42}'); + expect(JSON.stringify(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, description: 'description' }))).toEqual('{"name":"Name","types":["a","b"],"lat":42,"lng":42,"description":"description"}'); + expect(JSON.stringify(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, modifiers: ['c', 'd'] }))).toEqual('{"name":"Name","types":["a","b"],"lat":42,"lng":42,"modifiers":["c","d"]}'); + expect(JSON.stringify(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, rate: 3 }))).toEqual('{"name":"Name","types":["a","b"],"lat":42,"lng":42,"rate":3}'); + expect(JSON.stringify(Utils.formatMarker({ name: 'Name', types: ['a', 'b'], lat: 42, lng: 42, price: 1 }))).toEqual('{"name":"Name","types":["a","b"],"lat":42,"lng":42,"price":1}'); + // End formatting tests + done(); + }); + + + it('Public static method removeAllObjectKeys', done => { + expect(typeof Utils.removeAllObjectKeys).toEqual('function'); + // Invalid argument args (empty) + expect(Utils.removeAllObjectKeys()).toEqual(false); + expect(Utils.removeAllObjectKeys(true)).toEqual(false); + expect(Utils.removeAllObjectKeys(0)).toEqual(false); + expect(Utils.removeAllObjectKeys('mark')).toEqual(false); + expect(Utils.removeAllObjectKeys([ true, false ])).toEqual(false); + expect(Utils.removeAllObjectKeys(() => {})).toEqual(false); + // Standard formatting behavior + const testObject = { + bool: true, + string: 'string', + number: 42, + array: [24, 42], + object: { false: true, true: false }, + function: () => { return 42; } + }; + expect(Utils.removeAllObjectKeys(testObject)).toEqual(true); + expect(testObject).toEqual({}); + // End formatting tests + done(); + }); + + +}); diff --git a/front/test/templates/index.html b/front/test/templates/index.html new file mode 100644 index 0000000..eab1fac --- /dev/null +++ b/front/test/templates/index.html @@ -0,0 +1,38 @@ + + + + + Beer Crackerz + + + + + + + +
        +

        +
        + +
        +
        +

        +

        +
        +
        +
        +

        +
        + + diff --git a/front/test/testContext.js b/front/test/testContext.js new file mode 100644 index 0000000..f11d55b --- /dev/null +++ b/front/test/testContext.js @@ -0,0 +1,2 @@ +const testContext = require.context('./spec/', true, /.spec.js$/); +testContext.keys().forEach(testContext); diff --git a/front/test/testStyle.css b/front/test/testStyle.css new file mode 100644 index 0000000..5c32176 --- /dev/null +++ b/front/test/testStyle.css @@ -0,0 +1,34 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + align-items: center; + display: flex; + flex-direction: column-reverse; + justify-content: flex-end; + width: 100vw; + overflow-x: hidden; +} + +.container { + align-items: center; + background-color: rgba(0, 0, 0, 0.1); + display: flex; + justify-content: center; + height: 600px; + overflow: hidden; + position: absolute; + width: 800px; + margin: 15px auto; +} + +.jasmine_html-reporter { + bottom: 0; + margin: 0px; + margin-bottom: 30px; + position: absolute; + width: 96%; +} diff --git a/webpack/.eslintrc b/front/webpack/.eslintrc similarity index 100% rename from webpack/.eslintrc rename to front/webpack/.eslintrc diff --git a/front/webpack/jsDoc.json b/front/webpack/jsDoc.json new file mode 100644 index 0000000..aeb84c7 --- /dev/null +++ b/front/webpack/jsDoc.json @@ -0,0 +1,39 @@ +{ + "plugins": ["plugins/markdown"], + "recurseDepth": 10, + "source": { + "include": ["./src/"], + "exclude": ["./dist/", "./doc/", "./node_modules/", "./test/"], + "includePattern": ".+\\.js(doc|x)?$", + "excludePattern": "(^|\\/|\\\\)_" + }, + "sourceType": "module", + "tags": { + "allowUnknownTags": true, + "dictionaries": ["jsdoc", "closure"] + }, + "templates": { + "name": "BeerCrackerz", + "footerText": "BeerCrackerz", + "cleverLinks": false, + "monospaceLinks": false, + "logo": { + "url": "../static/img/logo.png", + "width": "20px", + "height": "20px", + "link": "https://github.com/MesseBasseProduction/BeerCrackerz" + } + }, + "opts": { + "destination": "./doc/", + "template": "templates/default", + "encoding": "utf8", + "readme": "../README.md", + "package": "", + "access": "all", + "recurse": true, + "verbose": true, + "private": true, + "pedantic": true + } +} diff --git a/webpack/loaders.js b/front/webpack/loaders.js similarity index 93% rename from webpack/loaders.js rename to front/webpack/loaders.js index 0c24a04..c072622 100644 --- a/webpack/loaders.js +++ b/front/webpack/loaders.js @@ -8,7 +8,7 @@ const CSSLoader = { { loader: MiniCssExtractPlugin.loader, options: { - publicPath: path.resolve(__dirname, '../assets/dist/') + publicPath: path.resolve(__dirname, './assets/dist/') } }, { diff --git a/webpack/plugins.js b/front/webpack/plugins.js similarity index 90% rename from webpack/plugins.js rename to front/webpack/plugins.js index e58c399..0366a90 100644 --- a/webpack/plugins.js +++ b/front/webpack/plugins.js @@ -11,13 +11,13 @@ const MiniCssExtractPlugin = new _MiniCssExtractPlugin({ const StyleLintPlugin = new _StyleLintPlugin({ configFile: path.resolve(__dirname, 'stylelint.config.js'), - context: path.resolve(__dirname, '../src'), + context: path.resolve(__dirname, './src'), files: '**/*.scss', }); const ESLintPlugin = new _ESLintPlugin({ overrideConfigFile: path.resolve(__dirname, '.eslintrc'), - context: path.resolve(__dirname, '../src/'), + context: path.resolve(__dirname, './src/'), files: '**/*.js' }); diff --git a/webpack/postcss.config.js b/front/webpack/postcss.config.js similarity index 100% rename from webpack/postcss.config.js rename to front/webpack/postcss.config.js diff --git a/webpack/stylelint.config.js b/front/webpack/stylelint.config.js similarity index 100% rename from webpack/stylelint.config.js rename to front/webpack/stylelint.config.js diff --git a/webpack/webpack.common.js b/front/webpack/webpack.common.js similarity index 81% rename from webpack/webpack.common.js rename to front/webpack/webpack.common.js index 06a37a6..e74f350 100644 --- a/webpack/webpack.common.js +++ b/front/webpack/webpack.common.js @@ -5,15 +5,15 @@ const plugins = require('./plugins'); module.exports = { entry: { - BeerCrackerz: ['./src/BeerCrackerz.js'], - BeerCrackerzAuth: ['./src/BeerCrackerzAuth.js'] + BeerCrackerz: ['./front/src/BeerCrackerz.js'], + BeerCrackerzAuth: ['./front/src/BeerCrackerzAuth.js'] }, module: { rules: [loaders.JSLoader, loaders.CSSLoader, loaders.FontLoader], }, output: { filename: '[name].bundle.js', - path: path.resolve(__dirname, '../assets/dist'), + path: path.resolve(__dirname, '../../static/dist'), library: 'BeerCrackerz', // We set a library name to bundle the export default of the class libraryTarget: 'window', // Make it globally available libraryExport: 'default', // Make BeerCrackerz.default become BeerCrackerz diff --git a/webpack/webpack.dev.js b/front/webpack/webpack.dev.js similarity index 100% rename from webpack/webpack.dev.js rename to front/webpack/webpack.dev.js diff --git a/webpack/webpack.prod.js b/front/webpack/webpack.prod.js similarity index 100% rename from webpack/webpack.prod.js rename to front/webpack/webpack.prod.js diff --git a/index.html b/index.html deleted file mode 100644 index c6dd815..0000000 --- a/index.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - Beer Crackerz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        -

        -
        - -
        -
        -

        +

        -
        -
        -
        -

        -
        -
        - - - - - - - - - diff --git a/login.html b/login.html deleted file mode 100644 index ebb100c..0000000 --- a/login.html +++ /dev/null @@ -1,74 +0,0 @@ - - - - - Login | BeerCrackerz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/package-lock.json b/package-lock.json index a2132ca..1398271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,47 +1,55 @@ { "name": "beercrackerz", - "version": "0.0.2", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "beercrackerz", - "version": "0.0.2", + "version": "0.1.0", "license": "GPL-3.0", "devDependencies": { - "@babel/core": "^7.18.10", - "@babel/preset-env": "^7.18.10", - "babel-loader": "^8.2.5", + "@babel/core": "^7.21.0", + "@babel/preset-env": "^7.20.2", + "babel-loader": "^9.1.2", "clean-webpack-plugin": "^4.0.0", - "css-loader": "^6.7.1", - "cssnano": "^5.1.13", - "eslint": "^8.22.0", - "eslint-webpack-plugin": "^3.2.0", + "css-loader": "^6.7.3", + "cssnano": "^5.1.15", + "eslint": "^8.36.0", + "eslint-webpack-plugin": "^4.0.0", "file-loader": "^6.2.0", - "http-server": "^14.1.1", - "jsdoc": "^3.6.11", - "mini-css-extract-plugin": "^2.6.1", - "node-sass": "^7.0.1", - "postcss": "^8.4.16", - "postcss-import": "^14.1.0", - "postcss-loader": "^7.0.1", - "postcss-preset-env": "^7.8.0", - "sass-loader": "^13.0.2", - "stylelint": "^14.10.0", - "stylelint-webpack-plugin": "^3.3.0", + "jasmine": "^4.5.0", + "jasmine-core": "^4.5.0", + "jsdoc": "^4.0.2", + "karma": "^6.4.1", + "karma-firefox-launcher": "^2.1.2", + "karma-html2js-preprocessor": "~1.1", + "karma-jasmine": "^5.1.0", + "karma-jasmine-html-reporter": "^2.0.0", + "karma-webpack": "^5.0.0", + "mini-css-extract-plugin": "^2.7.3", + "node-sass": "^8.0.0", + "postcss": "^8.4.21", + "postcss-import": "^15.1.0", + "postcss-loader": "^7.0.2", + "postcss-preset-env": "^8.0.1", + "sass-loader": "^13.2.0", + "stylelint": "^15.2.0", + "stylelint-webpack-plugin": "^4.1.0", "url-loader": "^4.1.1", - "webpack": "^5.74.0", - "webpack-cli": "^4.10.0", + "webpack": "^5.76.1", + "webpack-cli": "^5.0.1", "webpack-merge": "^5.8.0" } }, "node_modules/@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { "node": ">=6.0.0" @@ -60,34 +68,34 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.20.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", + "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "dev": true, "dependencies": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/generator": "^7.21.0", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" }, "engines": { @@ -99,19 +107,34 @@ } }, "node_modules/@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@babel/helper-annotate-as-pure": { "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", @@ -138,14 +161,15 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", "semver": "^6.3.0" }, "engines": { @@ -156,17 +180,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", - "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", + "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.20.7", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/helper-split-export-declaration": "^7.18.6" }, "engines": { @@ -177,13 +202,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "regexpu-core": "^5.2.1" }, "engines": { "node": ">=6.9.0" @@ -193,9 +218,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", - "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.17.7", @@ -231,13 +256,13 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -256,12 +281,12 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", + "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -280,19 +305,19 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" }, "engines": { "node": ">=6.9.0" @@ -311,9 +336,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true, "engines": { "node": ">=6.9.0" @@ -338,40 +363,41 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", - "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.20.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "dependencies": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -390,18 +416,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -417,29 +443,29 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz", - "integrity": "sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.18.9", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" }, "engines": { "node": ">=6.9.0" @@ -460,9 +486,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -487,14 +513,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", - "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -504,13 +530,13 @@ } }, "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" }, @@ -538,13 +564,13 @@ } }, "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", - "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", + "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -603,12 +629,12 @@ } }, "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -651,16 +677,16 @@ } }, "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", - "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -686,13 +712,13 @@ } }, "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", + "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -719,14 +745,14 @@ } }, "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -816,12 +842,12 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" }, "engines": { "node": ">=6.9.0" @@ -945,12 +971,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", - "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -960,14 +986,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" }, "engines": { "node": ">=6.9.0" @@ -992,12 +1018,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", - "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz", + "integrity": "sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1007,17 +1033,18 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", + "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" }, @@ -1029,12 +1056,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", - "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" }, "engines": { "node": ">=6.9.0" @@ -1044,12 +1072,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", - "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1168,14 +1196,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1185,15 +1212,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", + "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1203,16 +1229,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", "dev": true, "dependencies": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" }, "engines": { "node": ">=6.9.0" @@ -1238,13 +1263,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1285,12 +1310,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" }, "engines": { "node": ">=6.9.0" @@ -1315,13 +1340,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" }, "engines": { "node": ">=6.9.0" @@ -1361,13 +1386,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" }, "engines": { "node": ">=6.9.0" @@ -1453,18 +1478,18 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -1473,7 +1498,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -1484,7 +1509,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -1497,10 +1522,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -1508,30 +1533,30 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" }, "engines": { @@ -1558,45 +1583,45 @@ } }, "node_modules/@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1605,319 +1630,549 @@ } }, "node_modules/@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "dependencies": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@csstools/postcss-cascade-layers": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", - "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, - "dependencies": { - "@csstools/selector-specificity": "^2.0.2", - "postcss-selector-parser": "^6.0.10" - }, "engines": { - "node": "^12 || ^14 || >=16" + "node": ">=0.1.90" + } + }, + "node_modules/@csstools/cascade-layer-name-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.1.tgz", + "integrity": "sha512-SAAi5DpgJJWkfTvWSaqkgyIsTawa83hMwKrktkj6ra2h+q6ZN57vOGZ6ySHq6RSo+CbP64fA3aPChPBRDDUgtw==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0" } }, - "node_modules/@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "node_modules/@csstools/color-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-1.0.0.tgz", + "integrity": "sha512-tgqtiV8sU/VaWYjOB3O7PWs7HR/MmOLl2kTYRW2qSsTSEniJq7xmyAYFB1LPpXvvQcE5u2ih2dK9fyc8BnrAGQ==", "dev": true, - "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "engines": { + "node": "^14 || ^16 || >=18" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "node_modules/@csstools/css-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.0.0.tgz", + "integrity": "sha512-Xw0b/Jr+vLGGYD8cxsGWPaY5n1GtVC6G4tcga+eZPXZzRjjZHorPwW739UgtXzL2Da1RLxNE73c0r/KvmizPsw==", + "dev": true, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1" } }, - "node_modules/@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "node_modules/@csstools/css-parser-algorithms": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.0.1.tgz", + "integrity": "sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==", "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "@csstools/css-tokenizer": "^2.0.0" } }, - "node_modules/@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "node_modules/@csstools/css-tokenizer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.0.tgz", + "integrity": "sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==", "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" + "engines": { + "node": "^14 || ^16 || >=18" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "node_modules/@csstools/media-query-list-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.1.tgz", + "integrity": "sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==", + "dev": true, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0" } }, - "node_modules/@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "node_modules/@csstools/postcss-cascade-layers": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-3.0.1.tgz", + "integrity": "sha512-dD8W98dOYNOH/yX4V4HXOhfCOnvVAg8TtsL+qCGNoKXuq5z2C/d026wGWgySgC8cajXXo/wNezS31Glj5GcqrA==", "dev": true, "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "node_modules/@csstools/postcss-color-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-2.1.0.tgz", + "integrity": "sha512-XBoCClLyWchlYGHGlmMOa6M2UXZNrZm63HVfsvgD/z1RPm/s3+FhHyT6VkDo+OvEBPhCgn6xz4IeCu4pRctKDQ==", "dev": true, "dependencies": { - "@csstools/selector-specificity": "^2.0.0", - "postcss-selector-parser": "^6.0.10" + "@csstools/color-helpers": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-2.0.2.tgz", + "integrity": "sha512-iKYZlIs6JsNT7NKyRjyIyezTCHLh4L4BBB3F5Nx7Dc4Z/QmBgX+YJFuUSar8IM6KclGiAUFGomXFdYxAwJydlA==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "node_modules/@csstools/postcss-hwb-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-2.1.1.tgz", + "integrity": "sha512-XijKzdxBdH2hU6IcPWmnaU85FKEF1XE5hGy0d6dQC6XznFUIRu1T4uebL3krayX40m4xIcxfCBsQm5zphzVrtg==", "dev": true, "dependencies": { + "@csstools/color-helpers": "^1.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "node_modules/@csstools/postcss-ic-unit": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-2.0.2.tgz", + "integrity": "sha512-N84qGTJkfLTPj2qOG5P4CIqGjpZBbjOEMKMn+UjO5wlb9lcBTfBsxCF0lQsFdWJUzBHYFOz19dL66v71WF3Pig==", "dev": true, "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-3.1.1.tgz", + "integrity": "sha512-hhiacuby4YdUnnxfCYCRMBIobyJImozf0u+gHSbQ/tNOdwvmrZtVROvgW7zmfYuRkHVDNZJWZslq2v5jOU+j/A==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.3" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-stepped-value-functions": { + "node_modules/@csstools/postcss-logical-float-and-clear": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-1.0.1.tgz", + "integrity": "sha512-eO9z2sMLddvlfFEW5Fxbjyd03zaO7cJafDurK4rCqyRt9P7aaWwha0LcSzoROlcZrw1NBV2JAp2vMKfPMQO1xw==", "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "node_modules/@csstools/postcss-logical-resize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-1.0.1.tgz", + "integrity": "sha512-x1ge74eCSvpBkDDWppl+7FuD2dL68WP+wwP2qvdUcKY17vJksz+XoE1ZRV38uJgS6FNUwC0AxrPW5gy3MxsDHQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-trigonometric-functions": { + "node_modules/@csstools/postcss-logical-viewport-units": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-1.0.2.tgz", + "integrity": "sha512-nnKFywBqRMYjv5jyjSplD/nbAnboUEGFfdxKw1o34Y1nvycgqjQavhKkmxbORxroBBIDwC5y6SfgENcPPUcOxQ==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "@csstools/css-tokenizer": "^2.0.0" }, "engines": { - "node": "^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "node_modules/@csstools/postcss-media-queries-aspect-ratio-number-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-1.0.1.tgz", + "integrity": "sha512-V9yQqXdje6OfqDf6EL5iGOpi6N0OEczwYK83rql9UapQwFEryXlAehR5AqH8QqLYb6+y31wUXK6vMxCp0920Zg==", "dev": true, + "dependencies": { + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", + "@csstools/media-query-list-parser": "^2.0.0" + }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, - "node_modules/@csstools/selector-specificity": { + "node_modules/@csstools/postcss-nested-calc": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-2.0.2.tgz", + "integrity": "sha512-jbwrP8rN4e7LNaRcpx3xpMUjhtt34I9OV+zgbcsYAAk6k1+3kODXJBf95/JMYWhu9g1oif7r06QVUgfWsKxCFw==", "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2", - "postcss-selector-parser": "^6.0.10" + "postcss": "^8.4" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-2.0.1.tgz", + "integrity": "sha512-TQT5g3JQ5gPXC239YuRK8jFceXF9d25ZvBkyjzBGGoW5st5sPXFVQS8OjYb9IJ/K3CdfK4528y483cgS2DJR/w==", "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": ">=10.0.0" - } + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-2.1.0.tgz", + "integrity": "sha512-U/odSNjOVhagNRu+RDaNVbn8vaqA9GyCOoneQA2je7697KOrtRDc7/POrYsP7QioO2aaezDzKNX02wBzc99fkQ==", + "dev": true, + "dependencies": { + "@csstools/color-helpers": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-2.1.0.tgz", + "integrity": "sha512-tRX1rinsXajZlc4WiU7s9Y6O9EdSHScT997zDsvDUjQ1oZL2nvnL6Bt0s9KyQZZTdC3lrG2PIdBqdOIWXSEPlQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-scope-pseudo-class": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-2.0.2.tgz", + "integrity": "sha512-6Pvo4uexUCXt+Hz5iUtemQAcIuCYnL+ePs1khFR6/xPgC92aQLJ0zGHonWoewiBE+I++4gXK3pr+R1rlOFHe5w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-2.1.0.tgz", + "integrity": "sha512-CkEo9BF8fQeMoXW3biXjlgTLY7PA4UFihn6leq7hPoRzIguLUI0WZIVgsITGXfX8LXmkhCSTjXO2DLYu/LUixQ==", + "dev": true, + "dependencies": { + "@csstools/css-calc": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-2.2.1.tgz", + "integrity": "sha512-Ow6/cWWdjjVvA83mkm3kLRvvWsbzoe1AbJCxkpC+c9ibUjyS8pifm+LpZslQUKcxRVQ69ztKHDBEbFGTDhNeUw==", + "dev": true, + "dependencies": { + "@csstools/color-helpers": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-2.0.1.tgz", + "integrity": "sha512-uGmmVWGHozyWe6+I4w321fKUC034OB1OYW0ZP4ySHA23n+r9y93K+1yrmW+hThpSfApKhaWySoD4I71LLlFUYQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-2.0.1.tgz", + "integrity": "sha512-oJ9Xl29/yU8U7/pnMJRqAZd4YXNCfGEdcP4ywREuqm/xMqcgDNDppYRoCGDt40aaZQIEKBS79LytUDN/DHf0Ew==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", + "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", + "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } }, "node_modules/@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.5.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1926,12 +2181,15 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1955,6 +2213,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -1962,24 +2229,27 @@ "dev": true }, "node_modules/@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "dependencies": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "engines": { + "node": ">=12.22" + }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" @@ -1991,24 +2261,122 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@jest/types/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true, "engines": { "node": ">=6.0.0" @@ -2023,20 +2391,56 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jsdoc/salty": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.3.tgz", + "integrity": "sha512-bbtCxCkxcnWhi50I+4Lj6mdz9w3pOXOgEQrID8TCZ/DF51fW7M9GCQW2y45SpBDdHd1Eirm1X/Cf6CkAAe8HPg==", + "dev": true, + "dependencies": { + "lodash": "^4.17.21" + }, + "engines": { + "node": ">=v12.0.0" } }, "node_modules/@nodelib/fs.scandir": { @@ -2075,19 +2479,34 @@ } }, "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, "dependencies": { - "@gar/promisify": "^1.0.1", + "@gar/promisify": "^1.1.3", "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2099,17 +2518,24 @@ "node": ">=10" } }, + "node_modules/@npmcli/fs/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", "dev": true, "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/@npmcli/move-file/node_modules/rimraf": { @@ -2127,13 +2553,25 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true, "engines": { - "node": ">= 6" + "node": ">= 10" } }, "node_modules/@trysound/sax": { @@ -2145,10 +2583,25 @@ "node": ">=10.13.0" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", "dev": true, "dependencies": { "@types/estree": "*", @@ -2156,9 +2609,9 @@ } }, "node_modules/@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "dependencies": { "@types/eslint": "*", @@ -2166,9 +2619,9 @@ } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, "node_modules/@types/glob": { @@ -2181,6 +2634,30 @@ "@types/node": "*" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -2210,9 +2687,9 @@ "dev": true }, "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true }, "node_modules/@types/minimist": { @@ -2222,9 +2699,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "node_modules/@types/normalize-package-data": { @@ -2239,6 +2716,21 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/yargs": { + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, "node_modules/@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -2386,34 +2878,42 @@ } }, "node_modules/@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", + "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", "dev": true, + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x", - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", + "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", "dev": true, - "dependencies": { - "envinfo": "^7.7.3" + "engines": { + "node": ">=14.15.0" }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", + "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", "dev": true, + "engines": { + "node": ">=14.15.0" + }, "peerDependencies": { - "webpack-cli": "4.x.x" + "webpack": "5.x.x", + "webpack-cli": "5.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -2439,10 +2939,23 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -2495,6 +3008,15 @@ "node": ">= 8.0.0" } }, + "node_modules/agentkeepalive/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/aggregate-error": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", @@ -2542,9 +3064,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -2593,6 +3115,19 @@ "node": ">=4" } }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -2600,16 +3135,16 @@ "dev": true }, "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "dev": true, "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/argparse": { @@ -2621,7 +3156,7 @@ "node_modules/array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "dependencies": { "array-uniq": "^1.0.1" @@ -2633,7 +3168,7 @@ "node_modules/array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2642,30 +3177,12 @@ "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -2675,34 +3192,19 @@ "node": ">=8" } }, - "node_modules/async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "dependencies": { - "lodash": "^4.17.14" - } - }, "node_modules/async-foreach": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", + "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==", "dev": true, "engines": { "node": "*" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, "node_modules/autoprefixer": { - "version": "10.4.8", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", - "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", "dev": true, "funding": [ { @@ -2715,8 +3217,8 @@ } ], "dependencies": { - "browserslist": "^4.21.3", - "caniuse-lite": "^1.0.30001373", + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -2732,57 +3234,31 @@ "postcss": "^8.1.0" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, "node_modules/babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz", + "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==", "dev": true, "dependencies": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" + "find-cache-dir": "^3.3.2", + "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 8.9" + "node": ">= 14.15.0" }, "peerDependencies": { - "@babel/core": "^7.0.0", - "webpack": ">=2" - } - }, - "node_modules/babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "dependencies": { - "object.assign": "^4.1.0" + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", - "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "dependencies": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.2", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" }, "peerDependencies": { @@ -2790,25 +3266,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", - "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.2" + "@babel/helper-define-polyfill-provider": "^0.3.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" @@ -2820,25 +3296,13 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true, - "dependencies": { - "safe-buffer": "5.1.2" - }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" + "node": "^4.5.0 || >= 5.9" } }, "node_modules/big.js": { @@ -2850,12 +3314,60 @@ "node": "*" } }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -2885,9 +3397,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "funding": [ { @@ -2900,10 +3412,10 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" }, "bin": { "browserslist": "cli.js" @@ -2912,39 +3424,97 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", + "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "dev": true, + "engines": { + "node": ">=12" + } }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 10" + "node": ">=10" } }, "node_modules/cacache/node_modules/p-map": { @@ -2977,6 +3547,48 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/cacache/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/cacache/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3038,9 +3650,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001378", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz", - "integrity": "sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA==", + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==", "dev": true, "funding": [ { @@ -3053,12 +3665,6 @@ } ] }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, "node_modules/catharsis": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.9.0.tgz", @@ -3085,6 +3691,45 @@ "node": ">=4" } }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -3103,6 +3748,21 @@ "node": ">=6.0" } }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -3189,29 +3849,17 @@ } }, "node_modules/colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, "node_modules/colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -3224,72 +3872,111 @@ "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, + "node_modules/content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true, - "dependencies": { - "safe-buffer": "~5.1.1" + "engines": { + "node": ">= 0.6" } }, "node_modules/core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", "dev": true, "dependencies": { - "browserslist": "^4.21.3", - "semver": "7.0.0" + "browserslist": "^4.21.4" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/core-js" } }, - "node_modules/core-js-compat/node_modules/semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, "engines": { - "node": ">= 0.4.0" + "node": ">= 0.10" } }, "node_modules/cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "dependencies": { "@types/parse-json": "^4.0.0", @@ -3317,27 +4004,28 @@ } }, "node_modules/css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-5.0.2.tgz", + "integrity": "sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-blank-pseudo": "dist/cli.cjs" + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { "postcss": "^8.4" } }, "node_modules/css-declaration-sorter": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", - "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", "dev": true, "engines": { "node": "^10 || ^12 || >=14" @@ -3356,37 +4044,40 @@ } }, "node_modules/css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-5.0.2.tgz", + "integrity": "sha512-q+U+4QdwwB7T9VEW/LyO6CFrLAeLqOykC5mDqJXc7aKZAhDbq7BvGT13VGJe+IwBfdN2o3Xdw2kJ5IxwV1Sc9Q==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" - }, - "bin": { - "css-has-pseudo": "dist/cli.cjs" + "@csstools/selector-specificity": "^2.0.1", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { "postcss": "^8.4" } }, "node_modules/css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.7", + "postcss": "^8.4.19", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" + "semver": "^7.3.8" }, "engines": { "node": ">= 12.13.0" @@ -3399,10 +4090,22 @@ "webpack": "^5.0.0" } }, + "node_modules/css-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/css-loader/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -3414,16 +4117,23 @@ "node": ">=10" } }, + "node_modules/css-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-8.0.2.tgz", + "integrity": "sha512-OvFghizHJ45x7nsJJUSYLyQNTzsCU8yWjxAc/nhPQg1pbs18LMoET8N3kOweFDPy0JV0OSXN2iqRFhPBHYOeMA==", "dev": true, - "bin": { - "css-prefers-color-scheme": "dist/cli.cjs" - }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { "postcss": "^8.4" @@ -3446,16 +4156,16 @@ } }, "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/css-what": { @@ -3471,9 +4181,9 @@ } }, "node_modules/cssdb": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.0.tgz", - "integrity": "sha512-HmRYATZ4Gf8naf6sZmwKEyf7MXAC0ZxjsamtNNgmuWpQgoO973zsE/1JMIohEYsSi5e3n7vQauCLv7TWSrOlrw==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.4.1.tgz", + "integrity": "sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==", "dev": true, "funding": { "type": "opencollective", @@ -3493,12 +4203,12 @@ } }, "node_modules/cssnano": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.13.tgz", - "integrity": "sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ==", + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", "dev": true, "dependencies": { - "cssnano-preset-default": "^5.2.12", + "cssnano-preset-default": "^5.2.14", "lilconfig": "^2.0.3", "yaml": "^1.10.2" }, @@ -3514,25 +4224,25 @@ } }, "node_modules/cssnano-preset-default": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", - "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", "dev": true, "dependencies": { - "css-declaration-sorter": "^6.3.0", + "css-declaration-sorter": "^6.3.1", "cssnano-utils": "^3.1.0", "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.0", - "postcss-convert-values": "^5.1.2", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", "postcss-discard-comments": "^5.1.2", "postcss-discard-duplicates": "^5.1.0", "postcss-discard-empty": "^5.1.1", "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.6", - "postcss-merge-rules": "^5.1.2", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", "postcss-minify-font-values": "^5.1.0", "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.3", + "postcss-minify-params": "^5.1.4", "postcss-minify-selectors": "^5.2.1", "postcss-normalize-charset": "^5.1.0", "postcss-normalize-display-values": "^5.1.0", @@ -3540,11 +4250,11 @@ "postcss-normalize-repeat-style": "^5.1.1", "postcss-normalize-string": "^5.1.0", "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", "postcss-normalize-url": "^5.1.0", "postcss-normalize-whitespace": "^5.1.1", "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-initial": "^5.1.2", "postcss-reduce-transforms": "^5.1.0", "postcss-svgo": "^5.1.0", "postcss-unique-selectors": "^5.1.1" @@ -3580,16 +4290,38 @@ "node": ">=8.0.0" } }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "node_modules/csso/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "dev": true, "dependencies": { - "assert-plus": "^1.0.0" + "mdn-data": "2.0.14", + "source-map": "^0.6.1" }, "engines": { - "node": ">=0.10" + "node": ">=8.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" } }, "node_modules/debug": { @@ -3612,16 +4344,16 @@ "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, "dependencies": { "decamelize": "^1.1.0", @@ -3629,39 +4361,26 @@ }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/decamelize-keys/node_modules/map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=0.10.0" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, "node_modules/del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -3680,30 +4399,37 @@ "node": ">=6" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, "node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -3728,6 +4454,18 @@ "node": ">=6.0.0" } }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -3783,20 +4521,16 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.222", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.222.tgz", - "integrity": "sha512-gEM2awN5HZknWdLbngk4uQCVfhucFAfFzuchP3wM3NN6eow1eDU0dFy2kts43FB20ZfhVFF0jmFSTb1h5OhyIg==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "node_modules/emoji-regex": { @@ -3814,6 +4548,15 @@ "node": ">= 4" } }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -3824,10 +4567,53 @@ "iconv-lite": "^0.6.2" } }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -3837,6 +4623,12 @@ "node": ">=10.13.0" } }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, "node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -3897,6 +4689,12 @@ "node": ">=6" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -3907,14 +4705,18 @@ } }, "node_modules/eslint": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", - "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -3922,23 +4724,22 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", - "esquery": "^1.4.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -3946,11 +4747,9 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" @@ -3975,33 +4774,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint-visitor-keys": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", @@ -4012,120 +4784,29 @@ } }, "node_modules/eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-eM9ccGRWkU+btBSVfABRn8CjT7jZ2Q+UV/RfErMDVCFXpihEbvajNrLltZpwTAcEoXSqESGlEPIUxl7PoDlLWw==", "dev": true, "dependencies": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", + "@types/eslint": "^8.4.10", + "jest-worker": "^29.4.1", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0", + "eslint": "^8.0.0", "webpack": "^5.0.0" } }, - "node_modules/eslint-webpack-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/eslint-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -4141,15 +4822,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4175,95 +4847,20 @@ "color-name": "~1.1.4" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">=7.0.0" } }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, "engines": { "node": ">=10" }, @@ -4271,34 +4868,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/eslint/node_modules/globals": { + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "dependencies": { - "yocto-queue": "^0.1.0" + "type-fest": "^0.20.2" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, "node_modules/eslint/node_modules/supports-color": { @@ -4326,9 +4917,9 @@ } }, "node_modules/espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", "dev": true, "dependencies": { "acorn": "^8.8.0", @@ -4343,9 +4934,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -4405,15 +4996,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4421,9 +5003,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -4457,7 +5039,7 @@ "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "node_modules/fastest-levenshtein": { @@ -4470,9 +5052,9 @@ } }, "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -4540,6 +5122,51 @@ "node": ">=8" } }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -4558,16 +5185,19 @@ } }, "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { @@ -4599,15 +5229,15 @@ } }, "node_modules/flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true, "funding": [ { @@ -4624,29 +5254,6 @@ } } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -4660,6 +5267,20 @@ "url": "https://www.patreon.com/infusion" } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -4675,39 +5296,46 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "dev": true, "dependencies": { "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", + "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "wide-align": "^1.1.5" }, "engines": { - "node": ">=10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/gaze": { @@ -4741,9 +5369,9 @@ } }, "node_modules/get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "dependencies": { "function-bind": "^1.1.1", @@ -4757,31 +5385,22 @@ "node_modules/get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, @@ -4860,7 +5479,7 @@ "node_modules/globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "dependencies": { "array-union": "^1.0.1", @@ -4876,7 +5495,7 @@ "node_modules/globby/node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "engines": { "node": ">=0.10.0" @@ -4885,17 +5504,17 @@ "node_modules/globjoin": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, "node_modules/globule": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", - "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", + "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", "dev": true, "dependencies": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "^4.17.21", "minimatch": "~3.0.2" }, "engines": { @@ -4935,9 +5554,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "node_modules/grapheme-splitter": { @@ -4946,29 +5565,6 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -4999,18 +5595,6 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -5026,18 +5610,9 @@ "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -5050,18 +5625,24 @@ "node": ">=10" } }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "whatwg-encoding": "^2.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=12" + "node": ">=10" } }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/html-tags": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", @@ -5075,155 +5656,68 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "dev": true, - "dependencies": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "bin": { - "http-server": "bin/http-server" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/http-server/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/http-server/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/http-server/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "color-name": "~1.1.4" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/http-server/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/http-server/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "dev": true, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/http-server/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=8.0.0" } }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" + "node": ">= 6" } }, "node_modules/https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { "agent-base": "6", @@ -5236,19 +5730,19 @@ "node_modules/humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, "dependencies": { "ms": "^2.0.0" } }, "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { "node": ">=0.10.0" @@ -5267,9 +5761,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -5322,7 +5816,7 @@ "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" @@ -5346,7 +5840,7 @@ "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { "once": "^1.3.0", @@ -5366,30 +5860,42 @@ "dev": true }, "node_modules/interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -5398,10 +5904,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5431,7 +5952,7 @@ "node_modules/is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, "node_modules/is-number": { @@ -5464,7 +5985,7 @@ "node": ">=6" } }, - "node_modules/is-path-inside": { + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", @@ -5476,10 +5997,19 @@ "node": ">=6" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -5494,51 +6024,170 @@ "node": ">=0.10.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "dev": true }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "node_modules/jasmine": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.5.0.tgz", + "integrity": "sha512-9olGRvNZyADIwYL9XBNBst5BTU/YaePzuddK+YRslc7rI9MdTIE4r3xaBKbv2GEmzYYUfMOdTR8/i6JfLZaxSQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "jasmine-core": "^4.5.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.5.0.tgz", + "integrity": "sha512-9PMzyvhtocxb3aXJVOPqBDswdgyAeSB81QnLop4npOpbqnheaTEwPc9ZloQeVswugPManznQBjD8kWDTjlnHuw==", + "dev": true + }, + "node_modules/jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", "dev": true, "dependencies": { "@types/node": "*", + "jest-util": "^29.5.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, "engines": { - "node": ">= 10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker/node_modules/has-flag": { @@ -5571,6 +6220,16 @@ "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "dev": true }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5598,19 +6257,14 @@ "xmlcreate": "^2.0.4" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "node_modules/jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", "dev": true, "dependencies": { - "@babel/parser": "^7.9.4", + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", @@ -5623,7 +6277,6 @@ "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", "underscore": "~1.13.2" }, "bin": { @@ -5660,12 +6313,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -5675,40 +6322,164 @@ "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/karma": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", + "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-firefox-launcher": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz", + "integrity": "sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==", + "dev": true, + "dependencies": { + "is-wsl": "^2.2.0", + "which": "^2.0.1" + } + }, + "node_modules/karma-html2js-preprocessor": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/karma-html2js-preprocessor/-/karma-html2js-preprocessor-1.1.0.tgz", + "integrity": "sha512-SiaPXNxIQjzBnxbi0mOT24zCzjFWBGaxWM/DqnEhp4WbI5kNpiZ35Jb/h7etrob+wiDSuDkltCuxE/fMSayqeQ==", + "dev": true, + "peerDependencies": { + "karma": ">=0.9" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.0.0.tgz", + "integrity": "sha512-SB8HNNiazAHXM1vGEzf8/tSyEhkfxuDdhYdPBX2Mwgzt0OuF2gicApQ+uvXLID/gXyJQgvrM9+1/2SxZFUUDIA==", + "dev": true, + "peerDependencies": { + "jasmine-core": "^4.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-webpack": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz", + "integrity": "sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "webpack-merge": "^4.1.5" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/karma-webpack/node_modules/webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "minimist": "^1.2.6" }, - "engines": { - "node": ">=6" + "bin": { + "mkdirp": "bin/cmd.js" } }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "node_modules/karma/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "glob": "^7.1.3" }, - "engines": { - "node": ">=0.6.0" + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/kind-of": { @@ -5730,18 +6501,18 @@ } }, "node_modules/klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", "dev": true }, "node_modules/levn": { @@ -5758,9 +6529,9 @@ } }, "node_modules/lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true, "engines": { "node": ">=10" @@ -5782,18 +6553,18 @@ } }, "node_modules/loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true, "engines": { "node": ">=6.11.5" } }, "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "dependencies": { "big.js": "^5.2.2", @@ -5805,15 +6576,18 @@ } }, "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash": { @@ -5843,7 +6617,7 @@ "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "node_modules/lodash.uniq": { @@ -5852,16 +6626,29 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/log4js": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.1.tgz", + "integrity": "sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.3" }, "engines": { - "node": ">=10" + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" } }, "node_modules/make-dir": { @@ -5880,30 +6667,39 @@ } }, "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", + "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", + "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", + "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" }, "engines": { - "node": ">= 10" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", + "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "dev": true, + "engines": { + "node": ">=12" } }, "node_modules/map-obj": { @@ -5935,9 +6731,9 @@ } }, "node_modules/markdown-it-anchor": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz", - "integrity": "sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==", + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", + "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", "dev": true, "peerDependencies": { "@types/markdown-it": "*", @@ -5945,9 +6741,9 @@ } }, "node_modules/marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -5967,17 +6763,26 @@ } }, "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -6033,15 +6838,15 @@ } }, "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true, "bin": { "mime": "cli.js" }, "engines": { - "node": ">=4" + "node": ">=4.0.0" } }, "node_modules/mime-db": { @@ -6075,9 +6880,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", - "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.3.tgz", + "integrity": "sha512-CD9cXeKeXLcnMw8FZdtfrRrLaM7gwCl4nKuKn2YkY2Bw5wdlB8zU2cCzw+w2zS9RFvbrufTBkMCJACNPwqQA0w==", "dev": true, "dependencies": { "schema-utils": "^4.0.0" @@ -6093,59 +6898,6 @@ "webpack": "^5.0.0" } }, - "node_modules/mini-css-extract-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6159,10 +6911,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "dev": true + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/minimist-options": { "version": "4.1.0", @@ -6179,9 +6934,9 @@ } }, "node_modules/minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { "yallist": "^4.0.0" @@ -6203,20 +6958,20 @@ } }, "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, "dependencies": { - "minipass": "^3.1.0", + "minipass": "^3.1.6", "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" + "minizlib": "^2.1.2" }, "engines": { - "node": ">=8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" }, "optionalDependencies": { - "encoding": "^0.1.12" + "encoding": "^0.1.13" } }, "node_modules/minipass-flush": { @@ -6255,6 +7010,12 @@ "node": ">=8" } }, + "node_modules/minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", @@ -6268,6 +7029,12 @@ "node": ">= 8" } }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -6287,9 +7054,9 @@ "dev": true }, "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", "dev": true }, "node_modules/nanoid": { @@ -6307,7 +7074,7 @@ "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/negotiator": { @@ -6316,84 +7083,184 @@ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, "engines": { - "node": ">= 0.6" + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/node-gyp/node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/node-gyp/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-gyp/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "node_modules/node-gyp/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "yallist": "^4.0.0" }, "engines": { - "node": ">= 10.12.0" + "node": ">=10" } }, - "node_modules/node-gyp/node_modules/are-we-there-yet": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", - "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", "dev": true, "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": ">= 10" } }, - "node_modules/node-gyp/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "node_modules/node-gyp/node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", "dev": true, "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" } }, - "node_modules/node-gyp/node_modules/npmlog": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", - "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", + "node_modules/node-gyp/node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.0", - "set-blocking": "^2.0.0" + "aggregate-error": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/node-gyp/node_modules/rimraf": { @@ -6412,9 +7279,9 @@ } }, "node_modules/node-gyp/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6426,16 +7293,66 @@ "node": ">=10" } }, + "node_modules/node-gyp/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/node-gyp/node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-gyp/node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/node-gyp/node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/node-gyp/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, "node_modules/node-sass": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.1.tgz", - "integrity": "sha512-uMy+Xt29NlqKCFdFRZyXKOTqGt+QaKHexv9STj2WeLottnlqZEEWx6Bj0MXNthmFRRdM/YwyNo/8Tr46TOM0jQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-8.0.0.tgz", + "integrity": "sha512-jPzqCF2/e6JXw6r3VxfIqYc8tKQdkj5Z/BDATYyG6FL6b/LuYBNFGFVhus0mthcWifHm/JzBpKAd+3eXsWeK/A==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -6446,20 +7363,19 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "lodash": "^4.17.15", + "make-fetch-happen": "^10.0.4", "meow": "^9.0.0", - "nan": "^2.13.2", + "nan": "^2.17.0", "node-gyp": "^8.4.1", - "npmlog": "^5.0.0", - "request": "^2.88.0", - "sass-graph": "4.0.0", + "sass-graph": "^4.0.1", "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "true-case-path": "^2.2.1" }, "bin": { "node-sass": "bin/node-sass" }, "engines": { - "node": ">=12" + "node": ">=14" } }, "node_modules/node-sass/node_modules/ansi-styles": { @@ -6562,10 +7478,22 @@ "node": ">=10" } }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -6577,6 +7505,12 @@ "node": ">=10" } }, + "node_modules/normalize-package-data/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6608,15 +7542,18 @@ } }, "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "dev": true, "dependencies": { - "are-we-there-yet": "^2.0.0", + "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", + "gauge": "^4.0.3", "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/nth-check": { @@ -6631,69 +7568,45 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "ee-first": "1.1.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.8" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "dependencies": { "wrappy": "1" } }, - "node_modules/opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true, - "bin": { - "opener": "bin/opener-bin.js" - } - }, "node_modules/optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -6712,30 +7625,33 @@ } }, "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-map": { @@ -6786,6 +7702,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6798,7 +7723,7 @@ "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6807,7 +7732,7 @@ "node_modules/path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "node_modules/path-key": { @@ -6834,12 +7759,6 @@ "node": ">=8" } }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -6870,7 +7789,7 @@ "node_modules/pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -6879,7 +7798,7 @@ "node_modules/pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "dependencies": { "pinkie": "^2.0.0" @@ -6900,45 +7819,62 @@ "node": ">=8" } }, - "node_modules/portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.12.0" + "node": ">=8" } }, - "node_modules/portfinder/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/portfinder/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "minimist": "^1.2.6" + "p-limit": "^2.2.0" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=8" } }, "node_modules/postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "dev": true, "funding": [ { @@ -6960,22 +7896,22 @@ } }, "node_modules/postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-6.0.2.tgz", + "integrity": "sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw==", "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-calc": { @@ -7007,34 +7943,34 @@ } }, "node_modules/postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-5.0.2.tgz", + "integrity": "sha512-M6ygxWOyd6eWf3sd1Lv8xi4SeF4iBPfJvkfMU4ITh8ExJc1qhbvh/U8Cv/uOvBgUVOMDdScvCdlg8+hREQzs7w==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-9.0.2.tgz", + "integrity": "sha512-SfPjgr//VQ/DOCf80STIAsdAs7sbIbxATvVmd+Ec7JvR8onz9pjawhq3BJM3Pie40EE3TyB0P6hft16D33Nlyg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", @@ -7045,31 +7981,31 @@ } }, "node_modules/postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-8.0.2.tgz", + "integrity": "sha512-xWf/JmAxVoB5bltHpXk+uGRoGFwu4WDAR7210el+iyvTdqiKpDhtcT8N3edXMoVJY0WHFMrKMUieql/wRNiXkw==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-colormin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", - "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", "dev": true, "dependencies": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", "colord": "^2.9.1", "postcss-value-parser": "^4.2.0" @@ -7082,12 +8018,12 @@ } }, "node_modules/postcss-convert-values": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", - "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", "dev": true, "dependencies": { - "browserslist": "^4.20.3", + "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -7098,34 +8034,40 @@ } }, "node_modules/postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.2.tgz", + "integrity": "sha512-osM9g4UKq4XKimAC7RAXroqi3BXpxfwTswAJQiZdrBjWGFGEyxQrY5H2eDWI8F+MEvEUfYDxA8scqi3QWROCSw==", "dev": true, "dependencies": { - "postcss-value-parser": "^4.2.0" + "@csstools/cascade-layer-name-parser": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", + "@csstools/media-query-list-parser": "^2.0.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.3" + "postcss": "^8.4" } }, "node_modules/postcss-custom-properties": { - "version": "12.1.8", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", - "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.1.4.tgz", + "integrity": "sha512-iSAdaZrM3KMec8cOSzeTUNXPYDlhqsMJHpt62yrjwG6nAnMtRHPk5JdMzGosBJtqEahDolvD5LNbcq+EZ78o5g==", "dev": true, "dependencies": { + "@csstools/cascade-layer-name-parser": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", @@ -7136,41 +8078,44 @@ } }, "node_modules/postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.2.tgz", + "integrity": "sha512-jX7VlE3jrgfBIOfxiGNRFq81xUoHSZhvxhQurzE7ZFRv+bUmMwB7/XnA0nNlts2CwNtbXm4Ozy0ZAYKHlCRmBQ==", "dev": true, "dependencies": { + "@csstools/cascade-layer-name-parser": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", "postcss-selector-parser": "^6.0.4" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.3" + "postcss": "^8.4" } }, "node_modules/postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-7.0.2.tgz", + "integrity": "sha512-cMnslilYxBf9k3qejnovrUONZx1rXeUZJw06fgIUBzABJe3D2LiLL5WAER7Imt3nrkaIgG05XZBztueLEf5P8w==", "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-discard-comments": { @@ -7222,65 +8167,58 @@ } }, "node_modules/postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-4.0.2.tgz", + "integrity": "sha512-GXL1RmFREDK4Q9aYvI2RhVrA6a6qqSMQQ5ke8gSH1xgV6exsqbcJpIumC7AOgooH6/WIG3/K/T8xxAiVHy/tJg==", "dev": true, "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, - "peerDependencies": { - "postcss": "^8.2" - } - }, - "node_modules/postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", - "dev": true, - "dependencies": { - "postcss-value-parser": "^4.2.0" - }, - "engines": { - "node": "^12 || ^14 || >=16" - }, "peerDependencies": { "postcss": "^8.4" } }, "node_modules/postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-8.0.2.tgz", + "integrity": "sha512-f/Vd+EC/GaKElknU59esVcRYr/Y3t1ZAQyL4u2xSOgkDy4bMCmG7VP5cGvj3+BTLNE9ETfEuz2nnt4qkZwTTeA==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { "postcss": "^8.4" } }, "node_modules/postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-7.0.2.tgz", + "integrity": "sha512-AHAJ89UQBcqBvFgQJE9XasGuwMNkKsGj4D/f9Uk60jFmEBHpAL14DrnSk3Rj+SwZTr/WUG+mh+Rvf8fid/346w==", "dev": true, "dependencies": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { "postcss": "^8.4" @@ -7296,44 +8234,44 @@ } }, "node_modules/postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-4.0.1.tgz", + "integrity": "sha512-V5OuQGw4lBumPlwHWk/PRfMKjaq/LTGR4WDTemIMCaMevArVfCCA9wBJiL1VjDAd+rzuCIlkRoRvDsSiAaZ4Fg==", "dev": true, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-5.0.2.tgz", + "integrity": "sha512-Sszjwo0ubETX0Fi5MvpYzsONwrsjeabjMoc5YqHvURFItXgIu3HdCjcVuVKGMPGzKRhgaknmdM5uVWInWPJmeg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", @@ -7341,7 +8279,7 @@ "resolve": "^1.1.7" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.0.0" }, "peerDependencies": { "postcss": "^8.0.0" @@ -7357,34 +8295,35 @@ } }, "node_modules/postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-5.1.0.tgz", + "integrity": "sha512-iZApRTNcpc71uTn7PkzjHtj5cmuZpvu6okX4jHnM5OFi2fG97sodjxkq6SpL65xhW0NviQrAMSX97ntyGVRV0w==", "dev": true, "dependencies": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/color-helpers": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-loader": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", - "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", "dev": true, "dependencies": { "cosmiconfig": "^7.0.0", "klona": "^2.0.5", - "semver": "^7.3.7" + "semver": "^7.3.8" }, "engines": { "node": ">= 14.15.0" @@ -7398,10 +8337,22 @@ "webpack": "^5.0.0" } }, + "node_modules/postcss-loader/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/postcss-loader/node_modules/semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -7413,13 +8364,26 @@ "node": ">=10" } }, + "node_modules/postcss-loader/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-6.1.0.tgz", + "integrity": "sha512-qb1+LpClhYjxac8SfOcWotnY3unKZesDqIOm+jnGt8rTl7xaIWpE2bPGZHxflOip1E/4ETo79qlJyRL3yrHn1g==", "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" }, "peerDependencies": { "postcss": "^8.4" @@ -7440,17 +8404,17 @@ "node_modules/postcss-media-query-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true }, "node_modules/postcss-merge-longhand": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", - "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.0" + "stylehacks": "^5.1.1" }, "engines": { "node": "^10 || ^12 || >=14.0" @@ -7460,12 +8424,12 @@ } }, "node_modules/postcss-merge-rules": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", - "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", "dev": true, "dependencies": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", "cssnano-utils": "^3.1.0", "postcss-selector-parser": "^6.0.5" @@ -7510,12 +8474,12 @@ } }, "node_modules/postcss-minify-params": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", - "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", "dev": true, "dependencies": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" }, @@ -7601,23 +8565,23 @@ } }, "node_modules/postcss-nesting": { - "version": "10.1.10", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", - "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-11.2.1.tgz", + "integrity": "sha512-E6Jq74Jo/PbRAtZioON54NPhUNJYxVWhwxbweYl1vAoBYuGlDIts5yhtKiZFLvkvwT73e/9nFrW3oMqAtgG+GQ==", "dev": true, "dependencies": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-normalize-charset": { @@ -7708,12 +8672,12 @@ } }, "node_modules/postcss-normalize-unicode": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", - "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", "dev": true, "dependencies": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" }, "engines": { @@ -7755,9 +8719,9 @@ } }, "node_modules/postcss-opacity-percentage": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", - "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", "dev": true, "funding": [ { @@ -7771,6 +8735,9 @@ ], "engines": { "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" } }, "node_modules/postcss-ordered-values": { @@ -7790,22 +8757,22 @@ } }, "node_modules/postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-4.0.1.tgz", + "integrity": "sha512-HQZ0qi/9iSYHW4w3ogNqVNr2J49DHJAl7r8O2p0Meip38jsdnRPgiDW7r/LlLrrMBMe3KHkvNtAV2UmRVxzLIg==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-page-break": { @@ -7818,117 +8785,121 @@ } }, "node_modules/postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-8.0.1.tgz", + "integrity": "sha512-Ow2LedN8sL4pq8ubukO77phSVt4QyCm35ZGCYXKvRFayAwcpgB0sjNJglDoTuRdUL32q/ZC1VkPBo0AOEr4Uiw==", "dev": true, "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-preset-env": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", - "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", - "dev": true, - "dependencies": { - "@csstools/postcss-cascade-layers": "^1.0.5", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.8", - "browserslist": "^4.21.3", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.0.0", - "postcss-attribute-case-insensitive": "^5.0.2", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-8.0.1.tgz", + "integrity": "sha512-IUbymw0JlUbyVG+I85963PNWgPp3KhnFa1sxU7M/2dGthxV8e297P0VV5W9XcyypoH4hirH2fp1c6fmqh6YnSg==", + "dev": true, + "dependencies": { + "@csstools/postcss-cascade-layers": "^3.0.0", + "@csstools/postcss-color-function": "^2.0.0", + "@csstools/postcss-font-format-keywords": "^2.0.0", + "@csstools/postcss-hwb-function": "^2.0.0", + "@csstools/postcss-ic-unit": "^2.0.0", + "@csstools/postcss-is-pseudo-class": "^3.0.0", + "@csstools/postcss-logical-float-and-clear": "^1.0.0", + "@csstools/postcss-logical-resize": "^1.0.0", + "@csstools/postcss-logical-viewport-units": "^1.0.0", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.0", + "@csstools/postcss-nested-calc": "^2.0.0", + "@csstools/postcss-normalize-display-values": "^2.0.0", + "@csstools/postcss-oklab-function": "^2.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", + "@csstools/postcss-scope-pseudo-class": "^2.0.0", + "@csstools/postcss-stepped-value-functions": "^2.0.0", + "@csstools/postcss-text-decoration-shorthand": "^2.0.0", + "@csstools/postcss-trigonometric-functions": "^2.0.0", + "@csstools/postcss-unset-value": "^2.0.0", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^5.0.0", + "css-has-pseudo": "^5.0.0", + "css-prefers-color-scheme": "^8.0.0", + "cssdb": "^7.4.0", + "postcss-attribute-case-insensitive": "^6.0.0", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.8", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", + "postcss-color-functional-notation": "^5.0.0", + "postcss-color-hex-alpha": "^9.0.0", + "postcss-color-rebeccapurple": "^8.0.0", + "postcss-custom-media": "^9.1.0", + "postcss-custom-properties": "^13.1.0", + "postcss-custom-selectors": "^7.1.0", + "postcss-dir-pseudo-class": "^7.0.0", + "postcss-double-position-gradients": "^4.0.0", + "postcss-focus-visible": "^8.0.0", + "postcss-focus-within": "^7.0.0", "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", + "postcss-gap-properties": "^4.0.0", + "postcss-image-set-function": "^5.0.0", "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", + "postcss-lab-function": "^5.0.0", + "postcss-logical": "^6.0.0", "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.10", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", + "postcss-nesting": "^11.0.0", + "postcss-opacity-percentage": "^1.1.3", + "postcss-overflow-shorthand": "^4.0.0", "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-place": "^8.0.0", + "postcss-pseudo-class-any-link": "^8.0.0", "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", + "postcss-selector-not": "^7.0.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-8.0.2.tgz", + "integrity": "sha512-FYTIuRE07jZ2CW8POvctRgArQJ43yxhr5vLmImdKUvjFCkR09kh8pIdlCwdx/jbFm7MiW4QP58L4oOUv3grQYA==", "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-reduce-initial": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", - "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", "dev": true, "dependencies": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" }, "engines": { @@ -7965,7 +8936,7 @@ "node_modules/postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", "dev": true }, "node_modules/postcss-safe-parser": { @@ -7985,28 +8956,28 @@ } }, "node_modules/postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-7.0.1.tgz", + "integrity": "sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ==", "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.10" }, "engines": { - "node": "^12 || ^14 || >=16" + "node": "^14 || ^16 || >=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/csstools" }, "peerDependencies": { - "postcss": "^8.2" + "postcss": "^8.4" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -8071,7 +9042,7 @@ "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, "node_modules/promise-retry": { @@ -8087,28 +9058,37 @@ "node": ">=10" } }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true, "engines": { "node": ">=6" } }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, "node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/queue-microtask": { @@ -8149,10 +9129,34 @@ "safe-buffer": "^5.1.0" } }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, "dependencies": { "pify": "^2.3.0" @@ -8161,7 +9165,7 @@ "node_modules/read-cache/node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8199,6 +9203,58 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/read-pkg-up/node_modules/type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -8258,16 +9314,28 @@ "node": ">= 6" } }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "dependencies": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/redent": { @@ -8290,9 +9358,9 @@ "dev": true }, "node_modules/regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "dependencies": { "regenerate": "^1.4.2" @@ -8302,59 +9370,47 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "node_modules/regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "dependencies": { "@babel/runtime": "^7.8.4" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/regexpu-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", - "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", "dev": true, "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" }, "engines": { "node": ">=4" } }, "node_modules/regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", "dev": true }, "node_modules/regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "dependencies": { "jsesc": "~0.5.0" @@ -8372,42 +9428,10 @@ "jsesc": "bin/jsesc" } }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" @@ -8425,25 +9449,25 @@ "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "node_modules/requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "dependencies": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "node_modules/resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "dependencies": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -8487,7 +9511,7 @@ "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true, "engines": { "node": ">= 4" @@ -8503,6 +9527,12 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -8539,10 +9569,24 @@ } }, "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, "node_modules/safer-buffer": { "version": "2.1.2", @@ -8551,14 +9595,14 @@ "dev": true }, "node_modules/sass-graph": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.0.tgz", - "integrity": "sha512-WSO/MfXqKH7/TS8RdkCX3lVkPFQzCgbqdGsmSKq6tlPU+GpGEsa/5aW18JqItnqh+lPtcjifqdZ/VmiILkKckQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.1.tgz", + "integrity": "sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==", "dev": true, "dependencies": { "glob": "^7.0.0", "lodash": "^4.17.11", - "scss-tokenizer": "^0.3.0", + "scss-tokenizer": "^0.4.3", "yargs": "^17.2.1" }, "bin": { @@ -8568,10 +9612,51 @@ "node": ">=12" } }, + "node_modules/sass-graph/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/sass-graph/node_modules/yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/sass-graph/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "dev": true, "dependencies": { "klona": "^2.0.4", @@ -8586,7 +9671,7 @@ }, "peerDependencies": { "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "sass": "^1.3.0", "sass-embedded": "*", "webpack": "^5.0.0" @@ -8607,48 +9692,77 @@ } }, "node_modules/schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "dependencies": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" }, "engines": { - "node": ">= 8.9.0" + "node": ">= 12.13.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" } }, + "node_modules/schema-utils/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/schema-utils/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/schema-utils/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/scss-tokenizer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.3.0.tgz", - "integrity": "sha512-14Zl9GcbBvOT9057ZKjpz5yPOyUWG2ojd9D5io28wHRYsOrs7U95Q+KNL87+32p8rc+LvDpbu/i9ZYjM9Q+FsQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz", + "integrity": "sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==", "dev": true, "dependencies": { - "js-base64": "^2.4.3", - "source-map": "^0.7.1" + "js-base64": "^2.4.9", + "source-map": "^0.7.3" } }, "node_modules/scss-tokenizer/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true, "engines": { "node": ">= 8" } }, - "node_modules/secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", - "dev": true - }, "node_modules/semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -8659,9 +9773,9 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -8670,7 +9784,13 @@ "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "node_modules/shallow-clone": { @@ -8706,6 +9826,20 @@ "node": ">=8" } }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -8781,13 +9915,49 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz", + "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.1", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true + }, + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socks": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", - "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dev": true, "dependencies": { - "ip": "^1.1.5", + "ip": "^2.0.0", "smart-buffer": "^4.2.0" }, "engines": { @@ -8796,14 +9966,14 @@ } }, "node_modules/socks-proxy-agent": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", - "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "dependencies": { "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { "node": ">= 10" @@ -8864,46 +10034,21 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, - "node_modules/sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "dependencies": { "minipass": "^3.1.1" }, "engines": { - "node": ">= 8" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/stable": { @@ -8913,6 +10058,15 @@ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", "dev": true }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/stdout-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", @@ -8937,6 +10091,12 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/stdout-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/stdout-stream/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8946,6 +10106,20 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/streamroller": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.4.tgz", + "integrity": "sha512-Ha1Ccw2/N5C/IF8Do6zgNe8F3jQo8MPBnMBGvX0QjNv/I97BcNRzK6/mzOpZHHK7DjMLTI3c7Xw7Y1KvdChkvw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8955,26 +10129,6 @@ "safe-buffer": "~5.2.0" } }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -9028,16 +10182,16 @@ "node_modules/style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", "dev": true }, "node_modules/stylehacks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", - "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", "dev": true, "dependencies": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" }, "engines": { @@ -9048,55 +10202,59 @@ } }, "node_modules/stylelint": { - "version": "14.10.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.10.0.tgz", - "integrity": "sha512-VAmyKrEK+wNFh9R8mNqoxEFzaa4gsHGhcT4xgkQDuOA5cjF6CaNS8loYV7gpi4tIZBPUyXesotPXzJAMN8VLOQ==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.2.0.tgz", + "integrity": "sha512-wjg5OLn8zQwjlj5cYUgyQpMWKzct42AG5dYlqkHRJQJqsystFFn3onqEc263KH4xfEI0W3lZCnlIhFfS64uwSA==", "dev": true, "dependencies": { - "@csstools/selector-specificity": "^2.0.2", + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1", + "@csstools/media-query-list-parser": "^2.0.1", + "@csstools/selector-specificity": "^2.1.1", "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", + "colord": "^2.9.3", + "cosmiconfig": "^8.0.0", "css-functions-list": "^3.1.0", + "css-tree": "^2.3.1", "debug": "^4.3.4", - "fast-glob": "^3.2.11", + "fast-glob": "^3.2.12", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.2.0", - "ignore": "^5.2.0", + "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", + "known-css-properties": "^0.26.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.16", + "postcss": "^8.4.21", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.10", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "style-search": "^0.1.0", - "supports-hyperlinks": "^2.2.0", + "supports-hyperlinks": "^2.3.0", "svg-tags": "^1.0.0", - "table": "^6.8.0", + "table": "^6.8.1", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.1" + "write-file-atomic": "^5.0.0" }, "bin": { "stylelint": "bin/stylelint.js" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": "^14.13.1 || >=16.0.0" }, "funding": { "type": "opencollective", @@ -9104,57 +10262,29 @@ } }, "node_modules/stylelint-webpack-plugin": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-3.3.0.tgz", - "integrity": "sha512-F53bapIZ9zI16ero8IWm6TrUE6SSibZBphJE9b5rR2FxtvmGmm1YmS+a5xjQzn63+cv71GVSCu4byX66fBLpEw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-Vm8H2uYflIiF9m4BjSBEn9cpqY2zZ0wDHgBxOVM6aWFDd0FvfNoymrSYYOIG5/ZST0NO/0NCXPWcpRVpv79Uew==", "dev": true, "dependencies": { "globby": "^11.1.0", - "jest-worker": "^28.1.0", + "jest-worker": "^29.4.2", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "stylelint": "^13.0.0 || ^14.0.0", + "stylelint": "^13.0.0 || ^14.0.0 || ^15.0.0", "webpack": "^5.0.0" } }, - "node_modules/stylelint-webpack-plugin/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/stylelint-webpack-plugin/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/stylelint-webpack-plugin/node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -9170,81 +10300,18 @@ "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/stylelint-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/stylelint-webpack-plugin/node_modules/jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" - } - }, - "node_modules/stylelint-webpack-plugin/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/stylelint-webpack-plugin/node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/stylelint-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/stylelint/node_modules/array-union": { @@ -9262,6 +10329,24 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.0.tgz", + "integrity": "sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg==", + "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + } + }, "node_modules/stylelint/node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -9304,9 +10389,9 @@ } }, "node_modules/supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, "dependencies": { "has-flag": "^4.0.0", @@ -9352,7 +10437,7 @@ "node_modules/svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "node_modules/svgo": { @@ -9376,10 +10461,29 @@ "node": ">=10.13.0" } }, + "node_modules/svgo/node_modules/css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "dependencies": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/svgo/node_modules/mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + }, "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -9393,9 +10497,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -9414,12 +10518,6 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", - "dev": true - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -9430,31 +10528,49 @@ } }, "node_modules/tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" }, "engines": { - "node": ">= 10" + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", "dev": true, "dependencies": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "bin": { @@ -9465,16 +10581,16 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "dependencies": { + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "engines": { "node": ">= 10.13.0" @@ -9498,6 +10614,29 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -9516,31 +10655,64 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/terser-webpack-plugin/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, - "node_modules/terser/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/tmp/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, "engines": { "node": ">=4" @@ -9558,17 +10730,13 @@ "node": ">=8.0" } }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, "engines": { - "node": ">=0.8" + "node": ">=0.6" } }, "node_modules/trim-newlines": { @@ -9581,30 +10749,9 @@ } }, "node_modules/true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "dev": true, - "dependencies": { - "glob": "^7.1.2" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", + "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==", "dev": true }, "node_modules/type-check": { @@ -9631,6 +10778,38 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.34.tgz", + "integrity": "sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -9638,9 +10817,9 @@ "dev": true }, "node_modules/underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -9666,57 +10845,69 @@ } }, "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, "engines": { "node": ">=4" } }, - "node_modules/union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "dependencies": { - "qs": "^6.4.0" + "unique-slug": "^3.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "dependencies": { - "unique-slug": "^2.0.0" + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" } }, "node_modules/update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "funding": [ { @@ -9748,12 +10939,6 @@ "punycode": "^2.1.0" } }, - "node_modules/url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, "node_modules/url-loader": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", @@ -9802,17 +10987,16 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true, - "bin": { - "uuid": "bin/uuid" + "engines": { + "node": ">= 0.4.0" } }, "node_modules/v8-compile-cache": { @@ -9831,18 +11015,22 @@ "spdx-expression-parse": "^3.0.0" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, "node_modules/watchpack": { @@ -9859,9 +11047,9 @@ } }, "node_modules/webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -9906,44 +11094,42 @@ } }, "node_modules/webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", + "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.0.1", + "@webpack-cli/info": "^2.0.1", + "@webpack-cli/serve": "^2.0.1", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^9.4.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.15.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "4.x.x || 5.x.x" + "webpack": "5.x.x" }, "peerDependenciesMeta": { "@webpack-cli/generators": { "optional": true }, - "@webpack-cli/migrate": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -9952,6 +11138,15 @@ } } }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/webpack-merge": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", @@ -9974,6 +11169,12 @@ "node": ">=10.13.0" } }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, "node_modules/webpack/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -10014,18 +11215,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -10118,20 +11307,41 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "node_modules/write-file-atomic": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", - "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, "node_modules/xmlcreate": { @@ -10150,9 +11360,9 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "node_modules/yaml": { @@ -10165,21 +11375,21 @@ } }, "node_modules/yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^4.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^20.2.2" }, "engines": { - "node": ">=12" + "node": ">=10" } }, "node_modules/yargs-parser": { @@ -10191,15 +11401,6 @@ "node": ">=10" } }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "dev": true, - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -10215,12 +11416,13 @@ }, "dependencies": { "@ampproject/remapping": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz", - "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.0" + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" } }, "@babel/code-frame": { @@ -10233,43 +11435,57 @@ } }, "@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.20.10", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.20.10.tgz", + "integrity": "sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==", "dev": true }, "@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz", + "integrity": "sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA==", "dev": true, "requires": { - "@ampproject/remapping": "^2.1.0", + "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", + "@babel/generator": "^7.21.0", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-module-transforms": "^7.21.0", + "@babel/helpers": "^7.21.0", + "@babel/parser": "^7.21.0", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", + "json5": "^2.2.2", "semver": "^6.3.0" } }, "@babel/generator": { - "version": "7.18.12", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", - "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "version": "7.21.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.1.tgz", + "integrity": "sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==", "dev": true, "requires": { - "@babel/types": "^7.18.10", + "@babel/types": "^7.21.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } } }, "@babel/helper-annotate-as-pure": { @@ -10292,46 +11508,48 @@ } }, "@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz", + "integrity": "sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", + "@babel/compat-data": "^7.20.5", "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", + "browserslist": "^4.21.3", + "lru-cache": "^5.1.1", "semver": "^6.3.0" } }, "@babel/helper-create-class-features-plugin": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz", - "integrity": "sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw==", + "version": "7.20.12", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.12.tgz", + "integrity": "sha512-9OunRkbT0JQcednL0UFvbfXpAsUXiGjUk0a7sN8fUXX7Mue79cUSMjHGDRRi/Vz9vYlpIhLV5fMD5dKoMhhsNQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.20.7", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-replace-supers": "^7.20.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/helper-split-export-declaration": "^7.18.6" } }, "@babel/helper-create-regexp-features-plugin": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.18.6.tgz", - "integrity": "sha512-7LcpH1wnQLGrI+4v+nPp+zUvIkF9x0ddv1Hkdue10tg3gmRnLy97DXh4STiOf1qeIInyD69Qv5kKSZzKD8B/7A==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "regexpu-core": "^5.1.0" + "regexpu-core": "^5.2.1" } }, "@babel/helper-define-polyfill-provider": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", - "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.17.7", @@ -10358,13 +11576,13 @@ } }, "@babel/helper-function-name": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", - "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz", + "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/types": "^7.21.0" } }, "@babel/helper-hoist-variables": { @@ -10377,12 +11595,12 @@ } }, "@babel/helper-member-expression-to-functions": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", - "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.20.7.tgz", + "integrity": "sha512-9J0CxJLq315fEdi4s7xK5TQaNYjZw+nDVpVqr1axNGKzdrdwYBD5b4uKv3n75aABG0rCCTK8Im8Ww7eYfMrZgw==", "dev": true, "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.7" } }, "@babel/helper-module-imports": { @@ -10395,19 +11613,19 @@ } }, "@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz", + "integrity": "sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.2", + "@babel/types": "^7.21.2" } }, "@babel/helper-optimise-call-expression": { @@ -10420,9 +11638,9 @@ } }, "@babel/helper-plugin-utils": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", - "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", "dev": true }, "@babel/helper-remap-async-to-generator": { @@ -10438,34 +11656,35 @@ } }, "@babel/helper-replace-supers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", - "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.20.7.tgz", + "integrity": "sha512-vujDMtB6LVfNW13jhlCrp48QNslK6JXi7lQG736HVbHz/mbf4Dc7tIRh1Xf5C0rF7BP8iiSxGMCmY6Ci1ven3A==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.20.7", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.20.2" } }, "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", - "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", "dev": true, "requires": { - "@babel/types": "^7.18.9" + "@babel/types": "^7.20.0" } }, "@babel/helper-split-export-declaration": { @@ -10478,15 +11697,15 @@ } }, "@babel/helper-string-parser": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", - "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "version": "7.19.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", - "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", "dev": true }, "@babel/helper-validator-option": { @@ -10496,26 +11715,26 @@ "dev": true }, "@babel/helper-wrap-function": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz", - "integrity": "sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", "dev": true, "requires": { - "@babel/helper-function-name": "^7.18.9", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" } }, "@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.0.tgz", + "integrity": "sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==", "dev": true, "requires": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.20.7", + "@babel/traverse": "^7.21.0", + "@babel/types": "^7.21.0" } }, "@babel/highlight": { @@ -10530,9 +11749,9 @@ } }, "@babel/parser": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.11.tgz", - "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.2.tgz", + "integrity": "sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -10545,24 +11764,24 @@ } }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", - "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz", + "integrity": "sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", - "@babel/plugin-proposal-optional-chaining": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-proposal-optional-chaining": "^7.20.7" } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", - "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", + "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", "dev": true, "requires": { "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-remap-async-to-generator": "^7.18.9", "@babel/plugin-syntax-async-generators": "^7.8.4" } @@ -10578,13 +11797,13 @@ } }, "@babel/plugin-proposal-class-static-block": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", - "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.20.7.tgz", + "integrity": "sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==", "dev": true, "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-class-static-block": "^7.14.5" } }, @@ -10619,12 +11838,12 @@ } }, "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", - "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" } }, @@ -10649,16 +11868,16 @@ } }, "@babel/plugin-proposal-object-rest-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", - "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.18.8" + "@babel/plugin-transform-parameters": "^7.20.7" } }, "@babel/plugin-proposal-optional-catch-binding": { @@ -10672,13 +11891,13 @@ } }, "@babel/plugin-proposal-optional-chaining": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", - "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.20.7.tgz", + "integrity": "sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", "@babel/plugin-syntax-optional-chaining": "^7.8.3" } }, @@ -10693,14 +11912,14 @@ } }, "@babel/plugin-proposal-private-property-in-object": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", - "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" } }, @@ -10760,12 +11979,12 @@ } }, "@babel/plugin-syntax-import-assertions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", - "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "version": "7.20.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.19.0" } }, "@babel/plugin-syntax-json-strings": { @@ -10850,23 +12069,23 @@ } }, "@babel/plugin-transform-arrow-functions": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", - "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.20.7.tgz", + "integrity": "sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-async-to-generator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", - "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", + "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-remap-async-to-generator": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9" } }, "@babel/plugin-transform-block-scoped-functions": { @@ -10879,46 +12098,48 @@ } }, "@babel/plugin-transform-block-scoping": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", - "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.11.tgz", + "integrity": "sha512-tA4N427a7fjf1P0/2I4ScsHGc5jcHPbb30xMbaTke2gxDuWpUfXDuX1FEymJwKk4tuGUvGcejAR6HdZVqmmPyw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-classes": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.9.tgz", - "integrity": "sha512-EkRQxsxoytpTlKJmSPYrsOMjCILacAjtSVkd4gChEe2kXjFCun3yohhW5I7plXJhCemM0gKsaGMcO8tinvCA5g==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.20.7.tgz", + "integrity": "sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==", "dev": true, "requires": { "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.7", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", "@babel/helper-optimise-call-expression": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.20.7", "@babel/helper-split-export-declaration": "^7.18.6", "globals": "^11.1.0" } }, "@babel/plugin-transform-computed-properties": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", - "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.20.7.tgz", + "integrity": "sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/template": "^7.20.7" } }, "@babel/plugin-transform-destructuring": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.9.tgz", - "integrity": "sha512-p5VCYNddPLkZTq4XymQIaIfZNJwT9YsjkPOhkVEqt6QIpQFZVM9IltqqYpOEkJoN1DPznmxUDyZ5CTZs/ZCuHA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.20.7.tgz", + "integrity": "sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-dotall-regex": { @@ -10989,39 +12210,36 @@ } }, "@babel/plugin-transform-modules-amd": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", - "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz", + "integrity": "sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-modules-commonjs": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", - "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.20.11.tgz", + "integrity": "sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==", "dev": true, "requires": { - "@babel/helper-module-transforms": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-simple-access": "^7.20.2" } }, "@babel/plugin-transform-modules-systemjs": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.9.tgz", - "integrity": "sha512-zY/VSIbbqtoRoJKo2cDTewL364jSlZGvn0LKOf9ntbfxOvjfmyrdtEEOAdswOswhZEb8UH3jDkCKHd1sPgsS0A==", + "version": "7.20.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz", + "integrity": "sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==", "dev": true, "requires": { "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-validator-identifier": "^7.18.6", - "babel-plugin-dynamic-import-node": "^2.3.3" + "@babel/helper-module-transforms": "^7.20.11", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-identifier": "^7.19.1" } }, "@babel/plugin-transform-modules-umd": { @@ -11035,13 +12253,13 @@ } }, "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.18.6.tgz", - "integrity": "sha512-UmEOGF8XgaIqD74bC8g7iV3RYj8lMf0Bw7NJzvnS9qQhM4mg+1WHKotUIdjxgD2RGrgFLZZPCFPFj3P/kVDYhg==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", "dev": true, "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-new-target": { @@ -11064,12 +12282,12 @@ } }, "@babel/plugin-transform-parameters": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", - "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.20.7.tgz", + "integrity": "sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6" + "@babel/helper-plugin-utils": "^7.20.2" } }, "@babel/plugin-transform-property-literals": { @@ -11082,13 +12300,13 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", - "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.6", - "regenerator-transform": "^0.15.0" + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" } }, "@babel/plugin-transform-reserved-words": { @@ -11110,13 +12328,13 @@ } }, "@babel/plugin-transform-spread": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.18.9.tgz", - "integrity": "sha512-39Q814wyoOPtIB/qGopNIL9xDChOE1pNU0ZY5dO0owhiVt/5kFm4li+/bBtwc7QotG0u5EPzqhZdjMtmqBqyQA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz", + "integrity": "sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==", "dev": true, "requires": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0" } }, "@babel/plugin-transform-sticky-regex": { @@ -11166,18 +12384,18 @@ } }, "@babel/preset-env": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", - "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "version": "7.20.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", "dev": true, "requires": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-plugin-utils": "^7.18.9", + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", "@babel/helper-validator-option": "^7.18.6", "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", "@babel/plugin-proposal-class-properties": "^7.18.6", "@babel/plugin-proposal-class-static-block": "^7.18.6", "@babel/plugin-proposal-dynamic-import": "^7.18.6", @@ -11186,7 +12404,7 @@ "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", "@babel/plugin-proposal-optional-chaining": "^7.18.9", "@babel/plugin-proposal-private-methods": "^7.18.6", @@ -11197,7 +12415,7 @@ "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.18.6", + "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", @@ -11210,10 +12428,10 @@ "@babel/plugin-transform-arrow-functions": "^7.18.6", "@babel/plugin-transform-async-to-generator": "^7.18.6", "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.18.9", - "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", "@babel/plugin-transform-dotall-regex": "^7.18.6", "@babel/plugin-transform-duplicate-keys": "^7.18.9", "@babel/plugin-transform-exponentiation-operator": "^7.18.6", @@ -11221,30 +12439,30 @@ "@babel/plugin-transform-function-name": "^7.18.9", "@babel/plugin-transform-literals": "^7.18.9", "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.18.6", - "@babel/plugin-transform-modules-commonjs": "^7.18.6", - "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", "@babel/plugin-transform-new-target": "^7.18.6", "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-parameters": "^7.20.1", "@babel/plugin-transform-property-literals": "^7.18.6", "@babel/plugin-transform-regenerator": "^7.18.6", "@babel/plugin-transform-reserved-words": "^7.18.6", "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-spread": "^7.19.0", "@babel/plugin-transform-sticky-regex": "^7.18.6", "@babel/plugin-transform-template-literals": "^7.18.9", "@babel/plugin-transform-typeof-symbol": "^7.18.9", "@babel/plugin-transform-unicode-escapes": "^7.18.10", "@babel/plugin-transform-unicode-regex": "^7.18.6", "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.18.10", - "babel-plugin-polyfill-corejs2": "^0.3.2", - "babel-plugin-polyfill-corejs3": "^0.5.3", - "babel-plugin-polyfill-regenerator": "^0.4.0", - "core-js-compat": "^3.22.1", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", "semver": "^6.3.0" } }, @@ -11262,58 +12480,104 @@ } }, "@babel/runtime": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", - "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", + "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", + "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7" } }, "@babel/traverse": { - "version": "7.18.11", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.11.tgz", - "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.2.tgz", + "integrity": "sha512-ts5FFU/dSUPS13tv8XiEObDu9K+iagEKME9kAbaP7r0Y9KtZJZ+NGndDvWoRAYNpeWafbpFeki3q9QoMD6gxyw==", "dev": true, "requires": { "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", + "@babel/generator": "^7.21.1", "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.18.9", + "@babel/helper-function-name": "^7.21.0", "@babel/helper-hoist-variables": "^7.18.6", "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.18.11", - "@babel/types": "^7.18.10", + "@babel/parser": "^7.21.2", + "@babel/types": "^7.21.2", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.10.tgz", - "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==", + "version": "7.21.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.2.tgz", + "integrity": "sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.18.10", - "@babel/helper-validator-identifier": "^7.18.6", + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", "to-fast-properties": "^2.0.0" } }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, + "@csstools/cascade-layer-name-parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.1.tgz", + "integrity": "sha512-SAAi5DpgJJWkfTvWSaqkgyIsTawa83hMwKrktkj6ra2h+q6ZN57vOGZ6ySHq6RSo+CbP64fA3aPChPBRDDUgtw==", + "dev": true, + "requires": {} + }, + "@csstools/color-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-1.0.0.tgz", + "integrity": "sha512-tgqtiV8sU/VaWYjOB3O7PWs7HR/MmOLl2kTYRW2qSsTSEniJq7xmyAYFB1LPpXvvQcE5u2ih2dK9fyc8BnrAGQ==", + "dev": true + }, + "@csstools/css-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-1.0.0.tgz", + "integrity": "sha512-Xw0b/Jr+vLGGYD8cxsGWPaY5n1GtVC6G4tcga+eZPXZzRjjZHorPwW739UgtXzL2Da1RLxNE73c0r/KvmizPsw==", + "dev": true, + "requires": {} + }, + "@csstools/css-parser-algorithms": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.0.1.tgz", + "integrity": "sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==", + "dev": true, + "requires": {} + }, + "@csstools/css-tokenizer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-2.1.0.tgz", + "integrity": "sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==", + "dev": true + }, + "@csstools/media-query-list-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.1.tgz", + "integrity": "sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==", + "dev": true, + "requires": {} + }, "@csstools/postcss-cascade-layers": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.5.tgz", - "integrity": "sha512-Id/9wBT7FkgFzdEpiEWrsVd4ltDxN0rI0QS0SChbeQiSuux3z21SJCRLu6h2cvCEUmaRi+VD0mHFj+GJD4GFnw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-3.0.1.tgz", + "integrity": "sha512-dD8W98dOYNOH/yX4V4HXOhfCOnvVAg8TtsL+qCGNoKXuq5z2C/d026wGWgySgC8cajXXo/wNezS31Glj5GcqrA==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.2", @@ -11321,128 +12585,179 @@ } }, "@csstools/postcss-color-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", - "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-2.1.0.tgz", + "integrity": "sha512-XBoCClLyWchlYGHGlmMOa6M2UXZNrZm63HVfsvgD/z1RPm/s3+FhHyT6VkDo+OvEBPhCgn6xz4IeCu4pRctKDQ==", "dev": true, "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/color-helpers": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-font-format-keywords": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", - "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-2.0.2.tgz", + "integrity": "sha512-iKYZlIs6JsNT7NKyRjyIyezTCHLh4L4BBB3F5Nx7Dc4Z/QmBgX+YJFuUSar8IM6KclGiAUFGomXFdYxAwJydlA==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-hwb-function": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", - "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-2.1.1.tgz", + "integrity": "sha512-XijKzdxBdH2hU6IcPWmnaU85FKEF1XE5hGy0d6dQC6XznFUIRu1T4uebL3krayX40m4xIcxfCBsQm5zphzVrtg==", "dev": true, "requires": { + "@csstools/color-helpers": "^1.0.0", "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-ic-unit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", - "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-2.0.2.tgz", + "integrity": "sha512-N84qGTJkfLTPj2qOG5P4CIqGjpZBbjOEMKMn+UjO5wlb9lcBTfBsxCF0lQsFdWJUzBHYFOz19dL66v71WF3Pig==", "dev": true, "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-is-pseudo-class": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", - "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-3.1.1.tgz", + "integrity": "sha512-hhiacuby4YdUnnxfCYCRMBIobyJImozf0u+gHSbQ/tNOdwvmrZtVROvgW7zmfYuRkHVDNZJWZslq2v5jOU+j/A==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.0", "postcss-selector-parser": "^6.0.10" } }, + "@csstools/postcss-logical-float-and-clear": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-float-and-clear/-/postcss-logical-float-and-clear-1.0.1.tgz", + "integrity": "sha512-eO9z2sMLddvlfFEW5Fxbjyd03zaO7cJafDurK4rCqyRt9P7aaWwha0LcSzoROlcZrw1NBV2JAp2vMKfPMQO1xw==", + "dev": true, + "requires": {} + }, + "@csstools/postcss-logical-resize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-resize/-/postcss-logical-resize-1.0.1.tgz", + "integrity": "sha512-x1ge74eCSvpBkDDWppl+7FuD2dL68WP+wwP2qvdUcKY17vJksz+XoE1ZRV38uJgS6FNUwC0AxrPW5gy3MxsDHQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-logical-viewport-units": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-logical-viewport-units/-/postcss-logical-viewport-units-1.0.2.tgz", + "integrity": "sha512-nnKFywBqRMYjv5jyjSplD/nbAnboUEGFfdxKw1o34Y1nvycgqjQavhKkmxbORxroBBIDwC5y6SfgENcPPUcOxQ==", + "dev": true, + "requires": { + "@csstools/css-tokenizer": "^2.0.0" + } + }, + "@csstools/postcss-media-queries-aspect-ratio-number-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-media-queries-aspect-ratio-number-values/-/postcss-media-queries-aspect-ratio-number-values-1.0.1.tgz", + "integrity": "sha512-V9yQqXdje6OfqDf6EL5iGOpi6N0OEczwYK83rql9UapQwFEryXlAehR5AqH8QqLYb6+y31wUXK6vMxCp0920Zg==", + "dev": true, + "requires": { + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", + "@csstools/media-query-list-parser": "^2.0.0" + } + }, "@csstools/postcss-nested-calc": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", - "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-2.0.2.tgz", + "integrity": "sha512-jbwrP8rN4e7LNaRcpx3xpMUjhtt34I9OV+zgbcsYAAk6k1+3kODXJBf95/JMYWhu9g1oif7r06QVUgfWsKxCFw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-normalize-display-values": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", - "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-2.0.1.tgz", + "integrity": "sha512-TQT5g3JQ5gPXC239YuRK8jFceXF9d25ZvBkyjzBGGoW5st5sPXFVQS8OjYb9IJ/K3CdfK4528y483cgS2DJR/w==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-oklab-function": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", - "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-2.1.0.tgz", + "integrity": "sha512-U/odSNjOVhagNRu+RDaNVbn8vaqA9GyCOoneQA2je7697KOrtRDc7/POrYsP7QioO2aaezDzKNX02wBzc99fkQ==", "dev": true, "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/color-helpers": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-progressive-custom-properties": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", - "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-2.1.0.tgz", + "integrity": "sha512-tRX1rinsXajZlc4WiU7s9Y6O9EdSHScT997zDsvDUjQ1oZL2nvnL6Bt0s9KyQZZTdC3lrG2PIdBqdOIWXSEPlQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, + "@csstools/postcss-scope-pseudo-class": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-scope-pseudo-class/-/postcss-scope-pseudo-class-2.0.2.tgz", + "integrity": "sha512-6Pvo4uexUCXt+Hz5iUtemQAcIuCYnL+ePs1khFR6/xPgC92aQLJ0zGHonWoewiBE+I++4gXK3pr+R1rlOFHe5w==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, "@csstools/postcss-stepped-value-functions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", - "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-2.1.0.tgz", + "integrity": "sha512-CkEo9BF8fQeMoXW3biXjlgTLY7PA4UFihn6leq7hPoRzIguLUI0WZIVgsITGXfX8LXmkhCSTjXO2DLYu/LUixQ==", "dev": true, "requires": { - "postcss-value-parser": "^4.2.0" + "@csstools/css-calc": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1" } }, "@csstools/postcss-text-decoration-shorthand": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", - "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-2.2.1.tgz", + "integrity": "sha512-Ow6/cWWdjjVvA83mkm3kLRvvWsbzoe1AbJCxkpC+c9ibUjyS8pifm+LpZslQUKcxRVQ69ztKHDBEbFGTDhNeUw==", "dev": true, "requires": { + "@csstools/color-helpers": "^1.0.0", "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-trigonometric-functions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", - "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-2.0.1.tgz", + "integrity": "sha512-uGmmVWGHozyWe6+I4w321fKUC034OB1OYW0ZP4ySHA23n+r9y93K+1yrmW+hThpSfApKhaWySoD4I71LLlFUYQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "@csstools/postcss-unset-value": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-2.0.1.tgz", + "integrity": "sha512-oJ9Xl29/yU8U7/pnMJRqAZd4YXNCfGEdcP4ywREuqm/xMqcgDNDppYRoCGDt40aaZQIEKBS79LytUDN/DHf0Ew==", "dev": true, "requires": {} }, "@csstools/selector-specificity": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", - "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", "dev": true, "requires": {} }, @@ -11452,16 +12767,31 @@ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, + "@eslint-community/eslint-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz", + "integrity": "sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.4.0.tgz", + "integrity": "sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==", + "dev": true + }, "@eslint/eslintrc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", - "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.1.tgz", + "integrity": "sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.3.2", - "globals": "^13.15.0", + "espree": "^9.5.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -11470,9 +12800,9 @@ }, "dependencies": { "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -11486,6 +12816,12 @@ } } }, + "@eslint/js": { + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.36.0.tgz", + "integrity": "sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==", + "dev": true + }, "@gar/promisify": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", @@ -11493,20 +12829,20 @@ "dev": true }, "@humanwhocodes/config-array": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.4.tgz", - "integrity": "sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw==", + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", "dev": true, "requires": { "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" } }, - "@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true }, "@humanwhocodes/object-schema": { @@ -11515,21 +12851,94 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.25.16" + } + }, + "@jest/types": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", + "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", + "dev": true, + "requires": { + "@jest/schemas": "^29.4.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, "@jridgewell/resolve-uri": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz", - "integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", "dev": true }, "@jridgewell/set-array": { @@ -11538,20 +12947,52 @@ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "dev": true }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, "@jridgewell/sourcemap-codec": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz", - "integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==", + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.14", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", - "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@jsdoc/salty": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@jsdoc/salty/-/salty-0.2.3.tgz", + "integrity": "sha512-bbtCxCkxcnWhi50I+4Lj6mdz9w3pOXOgEQrID8TCZ/DF51fW7M9GCQW2y45SpBDdHd1Eirm1X/Cf6CkAAe8HPg==", + "dev": true, + "requires": { + "lodash": "^4.17.21" } }, "@nodelib/fs.scandir": { @@ -11581,30 +13022,45 @@ } }, "@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", "dev": true, "requires": { - "@gar/promisify": "^1.0.1", + "@gar/promisify": "^1.1.3", "semver": "^7.3.5" }, "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", "dev": true, "requires": { "mkdirp": "^1.0.4", @@ -11622,10 +13078,22 @@ } } }, + "@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "dev": true }, "@trysound/sax": { @@ -11634,10 +13102,25 @@ "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", "dev": true }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/eslint": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.29.0.tgz", - "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", "dev": true, "requires": { "@types/estree": "*", @@ -11645,9 +13128,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.3", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", - "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", "dev": true, "requires": { "@types/eslint": "*", @@ -11655,9 +13138,9 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", "dev": true }, "@types/glob": { @@ -11670,6 +13153,30 @@ "@types/node": "*" } }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, "@types/json-schema": { "version": "7.0.11", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", @@ -11699,9 +13206,9 @@ "dev": true }, "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true }, "@types/minimist": { @@ -11711,9 +13218,9 @@ "dev": true }, "@types/node": { - "version": "17.0.23", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.23.tgz", - "integrity": "sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==", + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "dev": true }, "@types/normalize-package-data": { @@ -11728,6 +13235,21 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/yargs": { + "version": "17.0.22", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz", + "integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, "@webassemblyjs/ast": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", @@ -11875,25 +13397,23 @@ } }, "@webpack-cli/configtest": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", - "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", + "integrity": "sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A==", "dev": true, "requires": {} }, "@webpack-cli/info": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", - "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.1.tgz", + "integrity": "sha512-fE1UEWTwsAxRhrJNikE7v4EotYflkEhBL7EbajfkPlf6E37/2QshOy/D48Mw8G5XMFlQtS6YV42vtbG9zBpIQA==", "dev": true, - "requires": { - "envinfo": "^7.7.3" - } + "requires": {} }, "@webpack-cli/serve": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", - "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.1.tgz", + "integrity": "sha512-0G7tNyS+yW8TdgHwZKlDWYXFA6OJQnoLCQvYKkQP0Q2X205PSQ6RNUj0M+1OB/9gRQaUZ/ccYfaxd0nhaWKfjw==", "dev": true, "requires": {} }, @@ -11915,10 +13435,20 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-import-assertions": { @@ -11953,6 +13483,14 @@ "debug": "^4.1.0", "depd": "^1.1.2", "humanize-ms": "^1.2.1" + }, + "dependencies": { + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + } } }, "aggregate-error": { @@ -11987,9 +13525,9 @@ }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -12028,6 +13566,16 @@ "color-convert": "^1.9.0" } }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, "aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", @@ -12035,9 +13583,9 @@ "dev": true }, "are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", "dev": true, "requires": { "delegates": "^1.0.0", @@ -12053,7 +13601,7 @@ "array-union": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", "dev": true, "requires": { "array-uniq": "^1.0.1" @@ -12062,28 +13610,13 @@ "array-uniq": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", "dev": true }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true }, "astral-regex": { @@ -12092,102 +13625,64 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, "async-foreach": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz", - "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "integrity": "sha512-VUeSMD8nEGBWaZK4lizI1sf3yEC7pnAQ/mrI7pC2fBz2s/tq5jWWEngTwaf0Gruu/OoXRGLGg1XFqpYBiGTYJA==", "dev": true }, "autoprefixer": { - "version": "10.4.8", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.8.tgz", - "integrity": "sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==", + "version": "10.4.13", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", + "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", "dev": true, "requires": { - "browserslist": "^4.21.3", - "caniuse-lite": "^1.0.30001373", + "browserslist": "^4.21.4", + "caniuse-lite": "^1.0.30001426", "fraction.js": "^4.2.0", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "dev": true - }, "babel-loader": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", - "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", - "dev": true, - "requires": { - "find-cache-dir": "^3.3.1", - "loader-utils": "^2.0.0", - "make-dir": "^3.1.0", - "schema-utils": "^2.6.5" - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz", + "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==", "dev": true, "requires": { - "object.assign": "^4.1.0" + "find-cache-dir": "^3.3.2", + "schema-utils": "^4.0.0" } }, "babel-plugin-polyfill-corejs2": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", - "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", "dev": true, "requires": { "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.2", + "@babel/helper-define-polyfill-provider": "^0.3.3", "semver": "^6.1.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", - "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2", - "core-js-compat": "^3.21.0" + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", - "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.3.2" + "@babel/helper-define-polyfill-provider": "^0.3.3" } }, "balanced-match": { @@ -12196,23 +13691,11 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "requires": { - "safe-buffer": "5.1.2" - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true }, "big.js": { "version": "5.2.2", @@ -12220,12 +13703,55 @@ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, "bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", "dev": true }, + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -12252,15 +13778,15 @@ } }, "browserslist": { - "version": "4.21.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", - "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001370", - "electron-to-chromium": "^1.4.202", + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.5" + "update-browserslist-db": "^1.0.9" } }, "buffer-from": { @@ -12269,32 +13795,75 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, "cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", "dev": true, "requires": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" }, "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "lru-cache": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", + "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "dev": true + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, "p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -12311,6 +13880,41 @@ "dev": true, "requires": { "glob": "^7.1.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } } } } @@ -12361,15 +13965,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001378", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz", - "integrity": "sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA==", - "dev": true - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "version": "1.0.30001446", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz", + "integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==", "dev": true }, "catharsis": { @@ -12392,6 +13990,33 @@ "supports-color": "^5.3.0" } }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -12404,6 +14029,12 @@ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", "dev": true }, + "ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true + }, "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -12474,26 +14105,17 @@ "dev": true }, "colord": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz", - "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==", + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", "dev": true }, "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -12503,64 +14125,97 @@ "commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "convert-source-map": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", - "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true }, "core-js-compat": { - "version": "3.24.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.24.1.tgz", - "integrity": "sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw==", + "version": "3.27.2", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.27.2.tgz", + "integrity": "sha512-welaYuF7ZtbYKGrIy7y3eb40d37rG1FvzEOfe7hSLd2iD6duMDqUhRfSvCGyC46HhR6Y8JXXdZ2lnRUMkPBpvg==", "dev": true, "requires": { - "browserslist": "^4.21.3", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "browserslist": "^4.21.4" } }, "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha1-jtolLsqrWEDc2XXOuQ2TcMgZ/4c=", - "dev": true + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } }, "cosmiconfig": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz", - "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", "dev": true, "requires": { "@types/parse-json": "^4.0.0", @@ -12582,18 +14237,18 @@ } }, "css-blank-pseudo": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", - "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-5.0.2.tgz", + "integrity": "sha512-aCU4AZ7uEcVSUzagTlA9pHciz7aWPKA/YzrEkpdSopJ2pvhIxiQ5sYeMz1/KByxlIo4XBdvMNJAVKMg/GRnhfw==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" } }, "css-declaration-sorter": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.0.tgz", - "integrity": "sha512-OGT677UGHJTAVMRhPO+HJ4oKln3wkBTwtDFH0ojbqm+MJm6xuDMHp2nkhh/ThaBqq20IbraBQSWKfSLNHQO9Og==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", "dev": true, "requires": {} }, @@ -12604,45 +14259,62 @@ "dev": true }, "css-has-pseudo": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", - "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-5.0.2.tgz", + "integrity": "sha512-q+U+4QdwwB7T9VEW/LyO6CFrLAeLqOykC5mDqJXc7aKZAhDbq7BvGT13VGJe+IwBfdN2o3Xdw2kJ5IxwV1Sc9Q==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.9" + "@csstools/selector-specificity": "^2.0.1", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0" } }, "css-loader": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", - "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "version": "6.7.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", + "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", "dev": true, "requires": { "icss-utils": "^5.1.0", - "postcss": "^8.4.7", + "postcss": "^8.4.19", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.0", "postcss-modules-scope": "^3.0.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.5" + "semver": "^7.3.8" }, "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "css-prefers-color-scheme": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-8.0.2.tgz", + "integrity": "sha512-OvFghizHJ45x7nsJJUSYLyQNTzsCU8yWjxAc/nhPQg1pbs18LMoET8N3kOweFDPy0JV0OSXN2iqRFhPBHYOeMA==", "dev": true, "requires": {} }, @@ -12660,13 +14332,13 @@ } }, "css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", "dev": true, "requires": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" } }, "css-what": { @@ -12676,9 +14348,9 @@ "dev": true }, "cssdb": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.0.tgz", - "integrity": "sha512-HmRYATZ4Gf8naf6sZmwKEyf7MXAC0ZxjsamtNNgmuWpQgoO973zsE/1JMIohEYsSi5e3n7vQauCLv7TWSrOlrw==", + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.4.1.tgz", + "integrity": "sha512-0Q8NOMpXJ3iTDDbUv9grcmQAfdDx4qz+fN/+Md2FGbevT+6+bJNQ2LjB2YIUlLbpBTM32idU1Sb+tb/uGt6/XQ==", "dev": true }, "cssesc": { @@ -12688,36 +14360,36 @@ "dev": true }, "cssnano": { - "version": "5.1.13", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.13.tgz", - "integrity": "sha512-S2SL2ekdEz6w6a2epXn4CmMKU4K3KpcyXLKfAYc9UQQqJRkD/2eLUG0vJ3Db/9OvO5GuAdgXw3pFbR6abqghDQ==", + "version": "5.1.15", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", + "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", "dev": true, "requires": { - "cssnano-preset-default": "^5.2.12", + "cssnano-preset-default": "^5.2.14", "lilconfig": "^2.0.3", "yaml": "^1.10.2" } }, "cssnano-preset-default": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.12.tgz", - "integrity": "sha512-OyCBTZi+PXgylz9HAA5kHyoYhfGcYdwFmyaJzWnzxuGRtnMw/kR6ilW9XzlzlRAtB6PLT/r+prYgkef7hngFew==", + "version": "5.2.14", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", + "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", "dev": true, "requires": { - "css-declaration-sorter": "^6.3.0", + "css-declaration-sorter": "^6.3.1", "cssnano-utils": "^3.1.0", "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.0", - "postcss-convert-values": "^5.1.2", + "postcss-colormin": "^5.3.1", + "postcss-convert-values": "^5.1.3", "postcss-discard-comments": "^5.1.2", "postcss-discard-duplicates": "^5.1.0", "postcss-discard-empty": "^5.1.1", "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.6", - "postcss-merge-rules": "^5.1.2", + "postcss-merge-longhand": "^5.1.7", + "postcss-merge-rules": "^5.1.4", "postcss-minify-font-values": "^5.1.0", "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.3", + "postcss-minify-params": "^5.1.4", "postcss-minify-selectors": "^5.2.1", "postcss-normalize-charset": "^5.1.0", "postcss-normalize-display-values": "^5.1.0", @@ -12725,11 +14397,11 @@ "postcss-normalize-repeat-style": "^5.1.1", "postcss-normalize-string": "^5.1.0", "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.0", + "postcss-normalize-unicode": "^5.1.1", "postcss-normalize-url": "^5.1.0", "postcss-normalize-whitespace": "^5.1.1", "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.0", + "postcss-reduce-initial": "^5.1.2", "postcss-reduce-transforms": "^5.1.0", "postcss-svgo": "^5.1.0", "postcss-unique-selectors": "^5.1.1" @@ -12749,16 +14421,37 @@ "dev": true, "requires": { "css-tree": "^1.1.2" + }, + "dependencies": { + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + } } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true }, "debug": { "version": "4.3.4", @@ -12772,13 +14465,13 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true }, "decamelize-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", - "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, "requires": { "decamelize": "^1.1.0", @@ -12788,7 +14481,7 @@ "map-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true } } @@ -12799,16 +14492,6 @@ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "define-properties": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", - "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", - "dev": true, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, "del": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", @@ -12824,22 +14507,28 @@ "rimraf": "^2.6.3" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", "dev": true }, "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", "dev": true }, "dir-glob": { @@ -12860,6 +14549,18 @@ "esutils": "^2.0.2" } }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, "dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -12897,20 +14598,16 @@ "domhandler": "^4.2.0" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true }, "electron-to-chromium": { - "version": "1.4.222", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.222.tgz", - "integrity": "sha512-gEM2awN5HZknWdLbngk4uQCVfhucFAfFzuchP3wM3NN6eow1eDU0dFy2kts43FB20ZfhVFF0jmFSTb1h5OhyIg==", + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", "dev": true }, "emoji-regex": { @@ -12925,6 +14622,12 @@ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, "encoding": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", @@ -12933,18 +14636,60 @@ "optional": true, "requires": { "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "engine.io": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.1.tgz", + "integrity": "sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.2.3" } }, + "engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "dev": true + }, "enhanced-resolve": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", - "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", "dev": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, "entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", @@ -12990,6 +14735,12 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -12997,14 +14748,18 @@ "dev": true }, "eslint": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.22.0.tgz", - "integrity": "sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.0", - "@humanwhocodes/config-array": "^0.10.4", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "version": "8.36.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.36.0.tgz", + "integrity": "sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.1", + "@eslint/js": "8.36.0", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -13012,23 +14767,22 @@ "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.3", - "esquery": "^1.4.0", + "espree": "^9.5.0", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", @@ -13036,11 +14790,9 @@ "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "regexpp": "^3.2.0", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "text-table": "^0.2.0" }, "dependencies": { "ansi-styles": { @@ -13052,12 +14804,6 @@ "color-convert": "^2.0.1" } }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13089,72 +14835,21 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, "globals": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", - "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -13182,23 +14877,6 @@ "estraverse": "^5.2.0" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, "eslint-visitor-keys": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", @@ -13206,89 +14884,22 @@ "dev": true }, "eslint-webpack-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", - "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-4.0.0.tgz", + "integrity": "sha512-eM9ccGRWkU+btBSVfABRn8CjT7jZ2Q+UV/RfErMDVCFXpihEbvajNrLltZpwTAcEoXSqESGlEPIUxl7PoDlLWw==", "dev": true, "requires": { - "@types/eslint": "^7.29.0 || ^8.4.1", - "jest-worker": "^28.0.2", + "@types/eslint": "^8.4.10", + "jest-worker": "^29.4.1", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } } }, "espree": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.3.tgz", - "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.0.tgz", + "integrity": "sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==", "dev": true, "requires": { "acorn": "^8.8.0", @@ -13297,9 +14908,9 @@ } }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -13344,12 +14955,6 @@ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -13357,9 +14962,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", - "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -13389,7 +14994,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, "fastest-levenshtein": { @@ -13399,9 +15004,9 @@ "dev": true }, "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -13439,15 +15044,56 @@ } } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + } + } + }, "find-cache-dir": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", @@ -13460,12 +15106,12 @@ } }, "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^5.0.0", + "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, @@ -13491,40 +15137,34 @@ } }, "flatted": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz", - "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", "dev": true }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", "dev": true }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", "dev": true }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -13537,36 +15177,36 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, "gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", "dev": true, "requires": { "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", + "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" + "wide-align": "^1.1.5" } }, "gaze": { @@ -13591,9 +15231,9 @@ "dev": true }, "get-intrinsic": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", - "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", "dev": true, "requires": { "function-bind": "^1.1.1", @@ -13604,28 +15244,19 @@ "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", - "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "integrity": "sha512-F5aQMywwJ2n85s4hJPTT9RPxGmubonuB10MNYo17/xph174n2MIR33HRguhzVag10O/npM7SPk73LMZNP+FaWw==", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, "glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -13685,7 +15316,7 @@ "globby": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "integrity": "sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==", "dev": true, "requires": { "array-union": "^1.0.1", @@ -13698,7 +15329,7 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true } } @@ -13706,17 +15337,17 @@ "globjoin": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", - "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", "dev": true }, "globule": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.3.tgz", - "integrity": "sha512-mb1aYtDbIjTu4ShMB85m3UzjX9BVKe9WCzsnfMSZk+K5GpIbBOexgg4PPCt5eHDEG5/ZQAUX2Kct02zfiPLsKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", + "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", "dev": true, "requires": { "glob": "~7.1.1", - "lodash": "~4.17.10", + "lodash": "^4.17.21", "minimatch": "~3.0.2" }, "dependencies": { @@ -13746,9 +15377,9 @@ } }, "graceful-fs": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", - "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, "grapheme-splitter": { @@ -13757,22 +15388,6 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "dev": true, - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, "hard-rejection": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", @@ -13794,15 +15409,6 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, - "has-property-descriptors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", - "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.1" - } - }, "has-symbols": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", @@ -13812,13 +15418,7 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, "hosted-git-info": { @@ -13828,15 +15428,23 @@ "dev": true, "requires": { "lru-cache": "^6.0.0" - } - }, - "html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "requires": { - "whatwg-encoding": "^2.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "html-tags": { @@ -13846,11 +15454,32 @@ "dev": true }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, "http-proxy": { "version": "1.18.1", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", @@ -13863,103 +15492,20 @@ } }, "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "requires": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, - "http-server": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", - "integrity": "sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==", - "dev": true, - "requires": { - "basic-auth": "^2.0.1", - "chalk": "^4.1.2", - "corser": "^2.0.1", - "he": "^1.2.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy": "^1.18.1", - "mime": "^1.6.0", - "minimist": "^1.2.6", - "opener": "^1.5.1", - "portfinder": "^1.0.28", - "secure-compare": "3.0.1", - "union": "~0.5.0", - "url-join": "^4.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", - "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "requires": { "agent-base": "6", @@ -13969,19 +15515,19 @@ "humanize-ms": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", "dev": true, "requires": { "ms": "^2.0.0" } }, "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "safer-buffer": ">= 2.1.2 < 3" } }, "icss-utils": { @@ -13992,9 +15538,9 @@ "requires": {} }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -14026,7 +15572,7 @@ "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true }, "indent-string": { @@ -14044,7 +15590,7 @@ "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "requires": { "once": "^1.3.0", @@ -14064,36 +15610,51 @@ "dev": true }, "interpret": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", - "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true }, "ip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", "dev": true }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, "is-core-module": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz", - "integrity": "sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", "dev": true, "requires": { "has": "^1.0.3" } }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true }, "is-fullwidth-code-point": { @@ -14114,7 +15675,7 @@ "is-lambda": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, "is-number": { @@ -14136,21 +15697,29 @@ "dev": true, "requires": { "is-path-inside": "^2.1.0" + }, + "dependencies": { + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + } } }, "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", "dev": true }, "is-plain-object": { @@ -14159,43 +15728,128 @@ "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", "dev": true }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "jasmine": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.5.0.tgz", + "integrity": "sha512-9olGRvNZyADIwYL9XBNBst5BTU/YaePzuddK+YRslc7rI9MdTIE4r3xaBKbv2GEmzYYUfMOdTR8/i6JfLZaxSQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "jasmine-core": "^4.5.0" + } + }, + "jasmine-core": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.5.0.tgz", + "integrity": "sha512-9PMzyvhtocxb3aXJVOPqBDswdgyAeSB81QnLop4npOpbqnheaTEwPc9ZloQeVswugPManznQBjD8kWDTjlnHuw==", "dev": true }, + "jest-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", + "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", + "dev": true, + "requires": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", "dev": true, "requires": { "@types/node": "*", + "jest-util": "^29.5.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, @@ -14223,6 +15877,12 @@ "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", "dev": true }, + "js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -14247,19 +15907,14 @@ "xmlcreate": "^2.0.4" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, "jsdoc": { - "version": "3.6.11", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.11.tgz", - "integrity": "sha512-8UCU0TYeIYD9KeLzEcAu2q8N/mx9O3phAGl32nmHlE0LpaJL71mMkP4d+QE5zWfNt50qheHtOZ0qoxVrsX5TUg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.2.tgz", + "integrity": "sha512-e8cIg2z62InH7azBBi3EsSEqrKx+nUtAS5bBcYTSpZFA+vhNPyhv8PTFZ0WsjOPDj04/dOLlm08EDcQJDqaGQg==", "dev": true, "requires": { - "@babel/parser": "^7.9.4", + "@babel/parser": "^7.20.15", + "@jsdoc/salty": "^0.2.1", "@types/markdown-it": "^12.2.3", "bluebird": "^3.7.2", "catharsis": "^0.9.0", @@ -14272,7 +15927,6 @@ "mkdirp": "^1.0.4", "requizzle": "^0.2.3", "strip-json-comments": "^3.1.0", - "taffydb": "2.6.2", "underscore": "~1.13.2" }, "dependencies": { @@ -14296,12 +15950,6 @@ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -14311,31 +15959,129 @@ "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, - "jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "karma": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.1.tgz", + "integrity": "sha512-Cj57NKOskK7wtFWSlMvZf459iX+kpYIPXmkNUzP2WAFcA7nhr/ALn5R7sw3w+1udFDcpMx/tuB8d5amgm3ijaA==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "karma-firefox-launcher": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz", + "integrity": "sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA==", + "dev": true, + "requires": { + "is-wsl": "^2.2.0", + "which": "^2.0.1" + } + }, + "karma-html2js-preprocessor": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/karma-html2js-preprocessor/-/karma-html2js-preprocessor-1.1.0.tgz", + "integrity": "sha512-SiaPXNxIQjzBnxbi0mOT24zCzjFWBGaxWM/DqnEhp4WbI5kNpiZ35Jb/h7etrob+wiDSuDkltCuxE/fMSayqeQ==", + "dev": true, + "requires": {} + }, + "karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "requires": { + "jasmine-core": "^4.1.0" + } + }, + "karma-jasmine-html-reporter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.0.0.tgz", + "integrity": "sha512-SB8HNNiazAHXM1vGEzf8/tSyEhkfxuDdhYdPBX2Mwgzt0OuF2gicApQ+uvXLID/gXyJQgvrM9+1/2SxZFUUDIA==", + "dev": true, + "requires": {} + }, + "karma-webpack": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/karma-webpack/-/karma-webpack-5.0.0.tgz", + "integrity": "sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA==", "dev": true, "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "webpack-merge": "^4.1.5" + }, + "dependencies": { + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + } } }, "kind-of": { @@ -14354,15 +16100,15 @@ } }, "klona": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", - "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", "dev": true }, "known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", "dev": true }, "levn": { @@ -14376,9 +16122,9 @@ } }, "lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true }, "lines-and-columns": { @@ -14397,15 +16143,15 @@ } }, "loader-runner": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", - "integrity": "sha512-92+huvxMvYlMzMt0iIOukcwYBFpkYJdpl2xsZ7LrlayO7E8SOv+JJUEK17B/dJIHAOLMfh2dZZ/Y18WgmGtYNw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, "requires": { "big.js": "^5.2.2", @@ -14414,12 +16160,12 @@ } }, "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^4.1.0" + "p-locate": "^5.0.0" } }, "lodash": { @@ -14449,7 +16195,7 @@ "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "lodash.uniq": { @@ -14458,13 +16204,26 @@ "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true }, + "log4js": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.7.1.tgz", + "integrity": "sha512-lzbd0Eq1HRdWM2abSD7mk6YIVY0AogGJzb/z+lqzRk+8+XJP+M6L1MS5FUSc3jjGru4dbKjEMJmqlsoYYpuivQ==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.3" + } + }, "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "requires": { - "yallist": "^4.0.0" + "yallist": "^3.0.2" } }, "make-dir": { @@ -14477,27 +16236,35 @@ } }, "make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "requires": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", + "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", + "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", + "negotiator": "^0.6.3", "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.1.tgz", + "integrity": "sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA==", + "dev": true + } } }, "map-obj": { @@ -14520,16 +16287,16 @@ } }, "markdown-it-anchor": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.4.1.tgz", - "integrity": "sha512-sLODeRetZ/61KkKLJElaU3NuU2z7MhXf12Ml1WJMSdwpngeofneCRF+JBbat8HiSqhniOMuTemXMrsI7hA6XyA==", + "version": "8.6.6", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-8.6.6.tgz", + "integrity": "sha512-jRW30YGywD2ESXDc+l17AiritL0uVaSnWsb26f+68qaW9zgbIIr1f4v2Nsvc0+s0Z2N3uX6t/yAw7BwCQ1wMsA==", "dev": true, "requires": {} }, "marked": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.12.tgz", - "integrity": "sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==", + "version": "4.2.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.2.12.tgz", + "integrity": "sha512-yr8hSKa3Fv4D3jdZmtMMPghgVt6TWbk86WQaWhDloQjRSQhMMYCAro7jP7VDJrjjdV8pxVxMssXS8B8Y5DZ5aw==", "dev": true }, "mathml-tag-names": { @@ -14539,15 +16306,21 @@ "dev": true }, "mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", "dev": true }, "mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true }, "meow": { @@ -14593,9 +16366,9 @@ } }, "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", "dev": true }, "mime-db": { @@ -14620,53 +16393,12 @@ "dev": true }, "mini-css-extract-plugin": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", - "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.3.tgz", + "integrity": "sha512-CD9cXeKeXLcnMw8FZdtfrRrLaM7gwCl4nKuKn2YkY2Bw5wdlB8zU2cCzw+w2zS9RFvbrufTBkMCJACNPwqQA0w==", "dev": true, "requires": { "schema-utils": "^4.0.0" - }, - "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - } } }, "minimatch": { @@ -14679,9 +16411,9 @@ } }, "minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", "dev": true }, "minimist-options": { @@ -14696,12 +16428,20 @@ } }, "minipass": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.6.tgz", - "integrity": "sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "requires": { "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "minipass-collect": { @@ -14714,15 +16454,15 @@ } }, "minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", "dev": true, "requires": { - "encoding": "^0.1.12", - "minipass": "^3.1.0", + "encoding": "^0.1.13", + "minipass": "^3.1.6", "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" + "minizlib": "^2.1.2" } }, "minipass-flush": { @@ -14760,6 +16500,14 @@ "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "mkdirp": { @@ -14775,9 +16523,9 @@ "dev": true }, "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", "dev": true }, "nanoid": { @@ -14789,7 +16537,7 @@ "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "negotiator": { @@ -14822,42 +16570,121 @@ "which": "^2.0.2" }, "dependencies": { - "are-we-there-yet": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.0.tgz", - "integrity": "sha512-0GWpv50YSOcLXaN6/FAKY3vfRbllXWV2xvfA/oKJF8pzFhWXPV+yjhJXDBbjscDYowv7Yw1A3uigpzn5iEGTyw==", + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", "dev": true, "requires": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" } }, - "gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", "dev": true, "requires": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" } }, - "npmlog": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.1.tgz", - "integrity": "sha512-BTHDvY6nrRHuRfyjt1MAufLxYdVXZfd099H4+i1f0lPywNQyI4foeNXJRObB/uy+TYqUW0vAD9gbdSOXPst7Eg==", + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, "requires": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.0", - "set-blocking": "^2.0.0" + "aggregate-error": "^3.0.0" } }, "rimraf": { @@ -14870,26 +16697,70 @@ } }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" } + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "node-releases": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", - "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", + "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", "dev": true }, "node-sass": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-7.0.1.tgz", - "integrity": "sha512-uMy+Xt29NlqKCFdFRZyXKOTqGt+QaKHexv9STj2WeLottnlqZEEWx6Bj0MXNthmFRRdM/YwyNo/8Tr46TOM0jQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-8.0.0.tgz", + "integrity": "sha512-jPzqCF2/e6JXw6r3VxfIqYc8tKQdkj5Z/BDATYyG6FL6b/LuYBNFGFVhus0mthcWifHm/JzBpKAd+3eXsWeK/A==", "dev": true, "requires": { "async-foreach": "^0.1.3", @@ -14899,14 +16770,13 @@ "get-stdin": "^4.0.1", "glob": "^7.0.3", "lodash": "^4.17.15", + "make-fetch-happen": "^10.0.4", "meow": "^9.0.0", - "nan": "^2.13.2", + "nan": "^2.17.0", "node-gyp": "^8.4.1", - "npmlog": "^5.0.0", - "request": "^2.88.0", - "sass-graph": "4.0.0", + "sass-graph": "^4.0.1", "stdout-stream": "^1.4.0", - "true-case-path": "^1.0.2" + "true-case-path": "^2.2.1" }, "dependencies": { "ansi-styles": { @@ -14981,14 +16851,29 @@ "validate-npm-package-license": "^3.0.1" }, "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, @@ -15011,14 +16896,14 @@ "dev": true }, "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", "dev": true, "requires": { - "are-we-there-yet": "^2.0.0", + "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", + "gauge": "^4.0.3", "set-blocking": "^2.0.0" } }, @@ -15031,51 +16916,36 @@ "boolbase": "^1.0.0" } }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "dev": true }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", "dev": true }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" + "ee-first": "1.1.1" } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, "requires": { "wrappy": "1" } }, - "opener": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", - "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", - "dev": true - }, "optionator": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", @@ -15091,21 +16961,21 @@ } }, "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.2.0" + "p-limit": "^3.0.2" } }, "p-map": { @@ -15141,6 +17011,12 @@ "lines-and-columns": "^1.1.6" } }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -15150,13 +17026,13 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true }, "path-is-inside": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", "dev": true }, "path-key": { @@ -15177,12 +17053,6 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -15204,13 +17074,13 @@ "pinkie": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", "dev": true, "requires": { "pinkie": "^2.0.0" @@ -15223,43 +17093,51 @@ "dev": true, "requires": { "find-up": "^4.0.0" - } - }, - "portfinder": { - "version": "1.0.28", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz", - "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.5" }, "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "requires": { - "ms": "^2.1.1" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" } }, - "mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "requires": { - "minimist": "^1.2.6" + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" } } } }, "postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.21", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", + "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", "dev": true, "requires": { "nanoid": "^3.3.4", @@ -15268,9 +17146,9 @@ } }, "postcss-attribute-case-insensitive": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", - "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-6.0.2.tgz", + "integrity": "sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.10" @@ -15296,85 +17174,94 @@ } }, "postcss-color-functional-notation": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", - "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-5.0.2.tgz", + "integrity": "sha512-M6ygxWOyd6eWf3sd1Lv8xi4SeF4iBPfJvkfMU4ITh8ExJc1qhbvh/U8Cv/uOvBgUVOMDdScvCdlg8+hREQzs7w==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-color-hex-alpha": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", - "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-9.0.2.tgz", + "integrity": "sha512-SfPjgr//VQ/DOCf80STIAsdAs7sbIbxATvVmd+Ec7JvR8onz9pjawhq3BJM3Pie40EE3TyB0P6hft16D33Nlyg==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-color-rebeccapurple": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", - "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-8.0.2.tgz", + "integrity": "sha512-xWf/JmAxVoB5bltHpXk+uGRoGFwu4WDAR7210el+iyvTdqiKpDhtcT8N3edXMoVJY0WHFMrKMUieql/wRNiXkw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-colormin": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.0.tgz", - "integrity": "sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", + "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", "dev": true, "requires": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", "colord": "^2.9.1", "postcss-value-parser": "^4.2.0" } }, "postcss-convert-values": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz", - "integrity": "sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", + "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", "dev": true, "requires": { - "browserslist": "^4.20.3", + "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" } }, "postcss-custom-media": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", - "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-9.1.2.tgz", + "integrity": "sha512-osM9g4UKq4XKimAC7RAXroqi3BXpxfwTswAJQiZdrBjWGFGEyxQrY5H2eDWI8F+MEvEUfYDxA8scqi3QWROCSw==", "dev": true, "requires": { - "postcss-value-parser": "^4.2.0" + "@csstools/cascade-layer-name-parser": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", + "@csstools/media-query-list-parser": "^2.0.0" } }, "postcss-custom-properties": { - "version": "12.1.8", - "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", - "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-13.1.4.tgz", + "integrity": "sha512-iSAdaZrM3KMec8cOSzeTUNXPYDlhqsMJHpt62yrjwG6nAnMtRHPk5JdMzGosBJtqEahDolvD5LNbcq+EZ78o5g==", "dev": true, "requires": { + "@csstools/cascade-layer-name-parser": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", "postcss-value-parser": "^4.2.0" } }, "postcss-custom-selectors": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", - "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-7.1.2.tgz", + "integrity": "sha512-jX7VlE3jrgfBIOfxiGNRFq81xUoHSZhvxhQurzE7ZFRv+bUmMwB7/XnA0nNlts2CwNtbXm4Ozy0ZAYKHlCRmBQ==", "dev": true, "requires": { + "@csstools/cascade-layer-name-parser": "^1.0.0", + "@csstools/css-parser-algorithms": "^2.0.0", + "@csstools/css-tokenizer": "^2.0.0", "postcss-selector-parser": "^6.0.4" } }, "postcss-dir-pseudo-class": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", - "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-7.0.2.tgz", + "integrity": "sha512-cMnslilYxBf9k3qejnovrUONZx1rXeUZJw06fgIUBzABJe3D2LiLL5WAER7Imt3nrkaIgG05XZBztueLEf5P8w==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.10" @@ -15409,40 +17296,31 @@ "requires": {} }, "postcss-double-position-gradients": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", - "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", - "dev": true, - "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", - "postcss-value-parser": "^4.2.0" - } - }, - "postcss-env-function": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", - "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-4.0.2.tgz", + "integrity": "sha512-GXL1RmFREDK4Q9aYvI2RhVrA6a6qqSMQQ5ke8gSH1xgV6exsqbcJpIumC7AOgooH6/WIG3/K/T8xxAiVHy/tJg==", "dev": true, "requires": { + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" } }, "postcss-focus-visible": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", - "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-8.0.2.tgz", + "integrity": "sha512-f/Vd+EC/GaKElknU59esVcRYr/Y3t1ZAQyL4u2xSOgkDy4bMCmG7VP5cGvj3+BTLNE9ETfEuz2nnt4qkZwTTeA==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" } }, "postcss-focus-within": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", - "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-7.0.2.tgz", + "integrity": "sha512-AHAJ89UQBcqBvFgQJE9XasGuwMNkKsGj4D/f9Uk60jFmEBHpAL14DrnSk3Rj+SwZTr/WUG+mh+Rvf8fid/346w==", "dev": true, "requires": { - "postcss-selector-parser": "^6.0.9" + "postcss-selector-parser": "^6.0.10" } }, "postcss-font-variant": { @@ -15453,25 +17331,25 @@ "requires": {} }, "postcss-gap-properties": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-4.0.1.tgz", + "integrity": "sha512-V5OuQGw4lBumPlwHWk/PRfMKjaq/LTGR4WDTemIMCaMevArVfCCA9wBJiL1VjDAd+rzuCIlkRoRvDsSiAaZ4Fg==", "dev": true, "requires": {} }, "postcss-image-set-function": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", - "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-5.0.2.tgz", + "integrity": "sha512-Sszjwo0ubETX0Fi5MvpYzsONwrsjeabjMoc5YqHvURFItXgIu3HdCjcVuVKGMPGzKRhgaknmdM5uVWInWPJmeg==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-import": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", - "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", "dev": true, "requires": { "postcss-value-parser": "^4.0.0", @@ -15487,43 +17365,61 @@ "requires": {} }, "postcss-lab-function": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", - "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-5.1.0.tgz", + "integrity": "sha512-iZApRTNcpc71uTn7PkzjHtj5cmuZpvu6okX4jHnM5OFi2fG97sodjxkq6SpL65xhW0NviQrAMSX97ntyGVRV0w==", "dev": true, "requires": { - "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "@csstools/color-helpers": "^1.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", "postcss-value-parser": "^4.2.0" } }, "postcss-loader": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", - "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", + "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", "dev": true, "requires": { "cosmiconfig": "^7.0.0", "klona": "^2.0.5", - "semver": "^7.3.7" + "semver": "^7.3.8" }, "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "semver": { - "version": "7.3.7", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", - "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", "dev": true, "requires": { "lru-cache": "^6.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } }, "postcss-logical": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-6.1.0.tgz", + "integrity": "sha512-qb1+LpClhYjxac8SfOcWotnY3unKZesDqIOm+jnGt8rTl7xaIWpE2bPGZHxflOip1E/4ETo79qlJyRL3yrHn1g==", "dev": true, - "requires": {} + "requires": { + "postcss-value-parser": "^4.2.0" + } }, "postcss-media-minmax": { "version": "5.0.0", @@ -15535,26 +17431,26 @@ "postcss-media-query-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", - "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", "dev": true }, "postcss-merge-longhand": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.6.tgz", - "integrity": "sha512-6C/UGF/3T5OE2CEbOuX7iNO63dnvqhGZeUnKkDeifebY0XqkkvrctYSZurpNE902LDf2yKwwPFgotnfSoPhQiw==", + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", + "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.0" + "stylehacks": "^5.1.1" } }, "postcss-merge-rules": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz", - "integrity": "sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", + "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", "dev": true, "requires": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0", "cssnano-utils": "^3.1.0", "postcss-selector-parser": "^6.0.5" @@ -15581,12 +17477,12 @@ } }, "postcss-minify-params": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz", - "integrity": "sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", + "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", "dev": true, "requires": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "cssnano-utils": "^3.1.0", "postcss-value-parser": "^4.2.0" } @@ -15637,9 +17533,9 @@ } }, "postcss-nesting": { - "version": "10.1.10", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", - "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-11.2.1.tgz", + "integrity": "sha512-E6Jq74Jo/PbRAtZioON54NPhUNJYxVWhwxbweYl1vAoBYuGlDIts5yhtKiZFLvkvwT73e/9nFrW3oMqAtgG+GQ==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.0", @@ -15699,12 +17595,12 @@ } }, "postcss-normalize-unicode": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz", - "integrity": "sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", + "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", "dev": true, "requires": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "postcss-value-parser": "^4.2.0" } }, @@ -15728,10 +17624,11 @@ } }, "postcss-opacity-percentage": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", - "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", - "dev": true + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "dev": true, + "requires": {} }, "postcss-ordered-values": { "version": "5.1.3", @@ -15744,9 +17641,9 @@ } }, "postcss-overflow-shorthand": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", - "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-4.0.1.tgz", + "integrity": "sha512-HQZ0qi/9iSYHW4w3ogNqVNr2J49DHJAl7r8O2p0Meip38jsdnRPgiDW7r/LlLrrMBMe3KHkvNtAV2UmRVxzLIg==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" @@ -15760,87 +17657,91 @@ "requires": {} }, "postcss-place": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", - "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-8.0.1.tgz", + "integrity": "sha512-Ow2LedN8sL4pq8ubukO77phSVt4QyCm35ZGCYXKvRFayAwcpgB0sjNJglDoTuRdUL32q/ZC1VkPBo0AOEr4Uiw==", "dev": true, "requires": { "postcss-value-parser": "^4.2.0" } }, "postcss-preset-env": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", - "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", - "dev": true, - "requires": { - "@csstools/postcss-cascade-layers": "^1.0.5", - "@csstools/postcss-color-function": "^1.1.1", - "@csstools/postcss-font-format-keywords": "^1.0.1", - "@csstools/postcss-hwb-function": "^1.0.2", - "@csstools/postcss-ic-unit": "^1.0.1", - "@csstools/postcss-is-pseudo-class": "^2.0.7", - "@csstools/postcss-nested-calc": "^1.0.0", - "@csstools/postcss-normalize-display-values": "^1.0.1", - "@csstools/postcss-oklab-function": "^1.1.1", - "@csstools/postcss-progressive-custom-properties": "^1.3.0", - "@csstools/postcss-stepped-value-functions": "^1.0.1", - "@csstools/postcss-text-decoration-shorthand": "^1.0.0", - "@csstools/postcss-trigonometric-functions": "^1.0.2", - "@csstools/postcss-unset-value": "^1.0.2", - "autoprefixer": "^10.4.8", - "browserslist": "^4.21.3", - "css-blank-pseudo": "^3.0.3", - "css-has-pseudo": "^3.0.4", - "css-prefers-color-scheme": "^6.0.3", - "cssdb": "^7.0.0", - "postcss-attribute-case-insensitive": "^5.0.2", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-8.0.1.tgz", + "integrity": "sha512-IUbymw0JlUbyVG+I85963PNWgPp3KhnFa1sxU7M/2dGthxV8e297P0VV5W9XcyypoH4hirH2fp1c6fmqh6YnSg==", + "dev": true, + "requires": { + "@csstools/postcss-cascade-layers": "^3.0.0", + "@csstools/postcss-color-function": "^2.0.0", + "@csstools/postcss-font-format-keywords": "^2.0.0", + "@csstools/postcss-hwb-function": "^2.0.0", + "@csstools/postcss-ic-unit": "^2.0.0", + "@csstools/postcss-is-pseudo-class": "^3.0.0", + "@csstools/postcss-logical-float-and-clear": "^1.0.0", + "@csstools/postcss-logical-resize": "^1.0.0", + "@csstools/postcss-logical-viewport-units": "^1.0.0", + "@csstools/postcss-media-queries-aspect-ratio-number-values": "^1.0.0", + "@csstools/postcss-nested-calc": "^2.0.0", + "@csstools/postcss-normalize-display-values": "^2.0.0", + "@csstools/postcss-oklab-function": "^2.0.0", + "@csstools/postcss-progressive-custom-properties": "^2.0.0", + "@csstools/postcss-scope-pseudo-class": "^2.0.0", + "@csstools/postcss-stepped-value-functions": "^2.0.0", + "@csstools/postcss-text-decoration-shorthand": "^2.0.0", + "@csstools/postcss-trigonometric-functions": "^2.0.0", + "@csstools/postcss-unset-value": "^2.0.0", + "autoprefixer": "^10.4.13", + "browserslist": "^4.21.4", + "css-blank-pseudo": "^5.0.0", + "css-has-pseudo": "^5.0.0", + "css-prefers-color-scheme": "^8.0.0", + "cssdb": "^7.4.0", + "postcss-attribute-case-insensitive": "^6.0.0", "postcss-clamp": "^4.1.0", - "postcss-color-functional-notation": "^4.2.4", - "postcss-color-hex-alpha": "^8.0.4", - "postcss-color-rebeccapurple": "^7.1.1", - "postcss-custom-media": "^8.0.2", - "postcss-custom-properties": "^12.1.8", - "postcss-custom-selectors": "^6.0.3", - "postcss-dir-pseudo-class": "^6.0.5", - "postcss-double-position-gradients": "^3.1.2", - "postcss-env-function": "^4.0.6", - "postcss-focus-visible": "^6.0.4", - "postcss-focus-within": "^5.0.4", + "postcss-color-functional-notation": "^5.0.0", + "postcss-color-hex-alpha": "^9.0.0", + "postcss-color-rebeccapurple": "^8.0.0", + "postcss-custom-media": "^9.1.0", + "postcss-custom-properties": "^13.1.0", + "postcss-custom-selectors": "^7.1.0", + "postcss-dir-pseudo-class": "^7.0.0", + "postcss-double-position-gradients": "^4.0.0", + "postcss-focus-visible": "^8.0.0", + "postcss-focus-within": "^7.0.0", "postcss-font-variant": "^5.0.0", - "postcss-gap-properties": "^3.0.5", - "postcss-image-set-function": "^4.0.7", + "postcss-gap-properties": "^4.0.0", + "postcss-image-set-function": "^5.0.0", "postcss-initial": "^4.0.1", - "postcss-lab-function": "^4.2.1", - "postcss-logical": "^5.0.4", + "postcss-lab-function": "^5.0.0", + "postcss-logical": "^6.0.0", "postcss-media-minmax": "^5.0.0", - "postcss-nesting": "^10.1.10", - "postcss-opacity-percentage": "^1.1.2", - "postcss-overflow-shorthand": "^3.0.4", + "postcss-nesting": "^11.0.0", + "postcss-opacity-percentage": "^1.1.3", + "postcss-overflow-shorthand": "^4.0.0", "postcss-page-break": "^3.0.4", - "postcss-place": "^7.0.5", - "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-place": "^8.0.0", + "postcss-pseudo-class-any-link": "^8.0.0", "postcss-replace-overflow-wrap": "^4.0.0", - "postcss-selector-not": "^6.0.1", + "postcss-selector-not": "^7.0.0", "postcss-value-parser": "^4.2.0" } }, "postcss-pseudo-class-any-link": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", - "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-8.0.2.tgz", + "integrity": "sha512-FYTIuRE07jZ2CW8POvctRgArQJ43yxhr5vLmImdKUvjFCkR09kh8pIdlCwdx/jbFm7MiW4QP58L4oOUv3grQYA==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.10" } }, "postcss-reduce-initial": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz", - "integrity": "sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", + "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", "dev": true, "requires": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "caniuse-api": "^3.0.0" } }, @@ -15863,7 +17764,7 @@ "postcss-resolve-nested-selector": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", - "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "integrity": "sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==", "dev": true }, "postcss-safe-parser": { @@ -15874,18 +17775,18 @@ "requires": {} }, "postcss-selector-not": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", - "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-7.0.1.tgz", + "integrity": "sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ==", "dev": true, "requires": { "postcss-selector-parser": "^6.0.10" } }, "postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "version": "6.0.11", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", "dev": true, "requires": { "cssesc": "^3.0.0", @@ -15932,7 +17833,7 @@ "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", "dev": true }, "promise-retry": { @@ -15945,23 +17846,26 @@ "retry": "^0.12.0" } }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", "dev": true }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", "dev": true }, "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } }, "queue-microtask": { "version": "1.2.3", @@ -15984,10 +17888,28 @@ "safe-buffer": "^5.1.0" } }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", "dev": true, "requires": { "pify": "^2.3.0" @@ -15996,7 +17918,7 @@ "pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", "dev": true } } @@ -16056,6 +17978,43 @@ "type-fest": "^0.8.1" }, "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -16075,13 +18034,22 @@ "util-deprecate": "^1.0.1" } }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, "rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, "requires": { - "resolve": "^1.9.0" + "resolve": "^1.20.0" } }, "redent": { @@ -16101,59 +18069,53 @@ "dev": true }, "regenerate-unicode-properties": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz", - "integrity": "sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", "dev": true, "requires": { "regenerate": "^1.4.2" } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "regenerator-transform": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", - "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", "dev": true, "requires": { "@babel/runtime": "^7.8.4" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "regexpu-core": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", - "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", "dev": true, "requires": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.0.1", - "regjsgen": "^0.6.0", - "regjsparser": "^0.8.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.0.0" + "unicode-match-property-value-ecmascript": "^2.1.0" } }, "regjsgen": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.6.0.tgz", - "integrity": "sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", "dev": true }, "regjsparser": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.8.4.tgz", - "integrity": "sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", "dev": true, "requires": { "jsesc": "~0.5.0" @@ -16167,38 +18129,10 @@ } } }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true }, "require-from-string": { @@ -16210,25 +18144,25 @@ "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, "requizzle": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", - "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.4.tgz", + "integrity": "sha512-JRrFk1D4OQ4SqovXOgdav+K8EAhSB/LJZqCz8tbX0KObcdeM15Ss59ozWMBWmmINMagCwmqn4ZNryUGpBsl6Jw==", "dev": true, "requires": { - "lodash": "^4.17.14" + "lodash": "^4.17.21" } }, "resolve": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz", - "integrity": "sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==", + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", "dev": true, "requires": { - "is-core-module": "^2.8.1", + "is-core-module": "^2.9.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -16259,7 +18193,7 @@ "retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "dev": true }, "reusify": { @@ -16268,6 +18202,12 @@ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, "rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -16287,9 +18227,9 @@ } }, "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, "safer-buffer": { @@ -16299,21 +18239,55 @@ "dev": true }, "sass-graph": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.0.tgz", - "integrity": "sha512-WSO/MfXqKH7/TS8RdkCX3lVkPFQzCgbqdGsmSKq6tlPU+GpGEsa/5aW18JqItnqh+lPtcjifqdZ/VmiILkKckQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-4.0.1.tgz", + "integrity": "sha512-5YCfmGBmxoIRYHnKK2AKzrAkCoQ8ozO+iumT8K4tXJXRVCPf+7s1/9KxTSW3Rbvf+7Y7b4FR3mWyLnQr3PHocA==", "dev": true, "requires": { "glob": "^7.0.0", "lodash": "^4.17.11", - "scss-tokenizer": "^0.3.0", + "scss-tokenizer": "^0.4.3", "yargs": "^17.2.1" + }, + "dependencies": { + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "yargs": { + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + } } }, "sass-loader": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", - "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", + "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", "dev": true, "requires": { "klona": "^2.0.4", @@ -16321,40 +18295,64 @@ } }, "schema-utils": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", - "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", "dev": true, "requires": { - "@types/json-schema": "^7.0.5", - "ajv": "^6.12.4", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } } }, "scss-tokenizer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.3.0.tgz", - "integrity": "sha512-14Zl9GcbBvOT9057ZKjpz5yPOyUWG2ojd9D5io28wHRYsOrs7U95Q+KNL87+32p8rc+LvDpbu/i9ZYjM9Q+FsQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.4.3.tgz", + "integrity": "sha512-raKLgf1LI5QMQnG+RxHz6oK0sL3x3I4FN2UDLqgLOGO8hodECNnNh5BXn7fAyBxrA8zVzdQizQ6XjNJQ+uBwMw==", "dev": true, "requires": { - "js-base64": "^2.4.3", - "source-map": "^0.7.1" + "js-base64": "^2.4.9", + "source-map": "^0.7.3" }, "dependencies": { "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", "dev": true } } }, - "secure-compare": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz", - "integrity": "sha1-8aAymzCLIh+uN7mXTz1XjQypmeM=", - "dev": true - }, "semver": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", @@ -16362,9 +18360,9 @@ "dev": true }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -16373,7 +18371,13 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "dev": true }, "shallow-clone": { @@ -16400,6 +18404,17 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -16455,25 +18470,55 @@ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "dev": true }, + "socket.io": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.4.tgz", + "integrity": "sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.2.1", + "socket.io-adapter": "~2.4.0", + "socket.io-parser": "~4.2.1" + } + }, + "socket.io-adapter": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz", + "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg==", + "dev": true + }, + "socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, "socks": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.2.tgz", - "integrity": "sha512-zDZhHhZRY9PxRruRMR7kMhnf3I8hDs4S3f9RecfnGxvcBHQcKcIH/oUcEWffsfl1XxdYlA7nnlGbbTvPz9D8gA==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", "dev": true, "requires": { - "ip": "^1.1.5", + "ip": "^2.0.0", "smart-buffer": "^4.2.0" } }, "socks-proxy-agent": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", - "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "requires": { "agent-base": "^6.0.2", - "debug": "^4.3.1", - "socks": "^2.6.1" + "debug": "^4.3.3", + "socks": "^2.6.2" } }, "source-map": { @@ -16525,32 +18570,15 @@ } }, "spdx-license-ids": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", - "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", "dev": true }, - "sshpk": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", - "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", - "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - } - }, "ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", "dev": true, "requires": { "minipass": "^3.1.1" @@ -16562,6 +18590,12 @@ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", "dev": true }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + }, "stdout-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz", @@ -16586,6 +18620,12 @@ "util-deprecate": "~1.0.1" } }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -16597,6 +18637,17 @@ } } }, + "streamroller": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.4.tgz", + "integrity": "sha512-Ha1Ccw2/N5C/IF8Do6zgNe8F3jQo8MPBnMBGvX0QjNv/I97BcNRzK6/mzOpZHHK7DjMLTI3c7Xw7Y1KvdChkvw==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + } + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -16604,14 +18655,6 @@ "dev": true, "requires": { "safe-buffer": "~5.2.0" - }, - "dependencies": { - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - } } }, "string-width": { @@ -16652,63 +18695,67 @@ "style-search": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", - "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", "dev": true }, "stylehacks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", - "integrity": "sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", + "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", "dev": true, "requires": { - "browserslist": "^4.16.6", + "browserslist": "^4.21.4", "postcss-selector-parser": "^6.0.4" } }, "stylelint": { - "version": "14.10.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.10.0.tgz", - "integrity": "sha512-VAmyKrEK+wNFh9R8mNqoxEFzaa4gsHGhcT4xgkQDuOA5cjF6CaNS8loYV7gpi4tIZBPUyXesotPXzJAMN8VLOQ==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-15.2.0.tgz", + "integrity": "sha512-wjg5OLn8zQwjlj5cYUgyQpMWKzct42AG5dYlqkHRJQJqsystFFn3onqEc263KH4xfEI0W3lZCnlIhFfS64uwSA==", "dev": true, "requires": { - "@csstools/selector-specificity": "^2.0.2", + "@csstools/css-parser-algorithms": "^2.0.1", + "@csstools/css-tokenizer": "^2.0.1", + "@csstools/media-query-list-parser": "^2.0.1", + "@csstools/selector-specificity": "^2.1.1", "balanced-match": "^2.0.0", - "colord": "^2.9.2", - "cosmiconfig": "^7.0.1", + "colord": "^2.9.3", + "cosmiconfig": "^8.0.0", "css-functions-list": "^3.1.0", + "css-tree": "^2.3.1", "debug": "^4.3.4", - "fast-glob": "^3.2.11", + "fast-glob": "^3.2.12", "fastest-levenshtein": "^1.0.16", "file-entry-cache": "^6.0.1", "global-modules": "^2.0.0", "globby": "^11.1.0", "globjoin": "^0.1.4", "html-tags": "^3.2.0", - "ignore": "^5.2.0", + "ignore": "^5.2.4", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", + "known-css-properties": "^0.26.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.16", + "postcss": "^8.4.21", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.10", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "style-search": "^0.1.0", - "supports-hyperlinks": "^2.2.0", + "supports-hyperlinks": "^2.3.0", "svg-tags": "^1.0.0", - "table": "^6.8.0", + "table": "^6.8.1", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^4.0.1" + "write-file-atomic": "^5.0.0" }, "dependencies": { "array-union": { @@ -16723,6 +18770,18 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, + "cosmiconfig": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.1.0.tgz", + "integrity": "sha512-0tLZ9URlPGU7JsKq0DQOQ3FoRsYX8xDZ7xMiATQfaiGMz7EHowNkbU9u1coAOmnh9p/1ySpm0RB3JNWRXM5GCg==", + "dev": true, + "requires": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + } + }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -16746,39 +18805,18 @@ } }, "stylelint-webpack-plugin": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-3.3.0.tgz", - "integrity": "sha512-F53bapIZ9zI16ero8IWm6TrUE6SSibZBphJE9b5rR2FxtvmGmm1YmS+a5xjQzn63+cv71GVSCu4byX66fBLpEw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/stylelint-webpack-plugin/-/stylelint-webpack-plugin-4.1.0.tgz", + "integrity": "sha512-Vm8H2uYflIiF9m4BjSBEn9cpqY2zZ0wDHgBxOVM6aWFDd0FvfNoymrSYYOIG5/ZST0NO/0NCXPWcpRVpv79Uew==", "dev": true, "requires": { "globby": "^11.1.0", - "jest-worker": "^28.1.0", + "jest-worker": "^29.4.2", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "schema-utils": "^4.0.0" }, "dependencies": { - "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.3" - } - }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -16798,50 +18836,6 @@ "merge2": "^1.4.1", "slash": "^3.0.0" } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } } } }, @@ -16855,9 +18849,9 @@ } }, "supports-hyperlinks": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", - "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, "requires": { "has-flag": "^4.0.0", @@ -16890,7 +18884,7 @@ "svg-tags": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", - "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", "dev": true }, "svgo": { @@ -16906,12 +18900,30 @@ "csso": "^4.2.0", "picocolors": "^1.0.0", "stable": "^0.1.8" + }, + "dependencies": { + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "dev": true, + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + } + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==", + "dev": true + } } }, "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -16922,9 +18934,9 @@ }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -16941,12 +18953,6 @@ } } }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", - "dev": true - }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -16954,28 +18960,45 @@ "dev": true }, "tar": { - "version": "6.1.11", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", - "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.13.tgz", + "integrity": "sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==", "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", - "minipass": "^3.0.0", + "minipass": "^4.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.0.0.tgz", + "integrity": "sha512-g2Uuh2jEKoht+zvO6vJqXmYpflPqzRBT+Th2h01DKh5z7wbY/AZ2gCQ78cP70YoHPyFdY30YBV5WxgLOEwOykw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } } }, "terser": { - "version": "5.12.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.1.tgz", - "integrity": "sha512-NXbs+7nisos5E+yXwAD+y7zrcTkMqb0dEJxIGtSKPdCBzopf7ni4odPul2aechpV7EXNvOudYOX2bb5tln1jbQ==", + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", "dev": true, "requires": { + "@jridgewell/source-map": "^0.3.2", "acorn": "^8.5.0", "commander": "^2.20.0", - "source-map": "~0.7.2", "source-map-support": "~0.5.20" }, "dependencies": { @@ -16984,28 +19007,39 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true - }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true } } }, "terser-webpack-plugin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.1.tgz", - "integrity": "sha512-GvlZdT6wPQKbDNW/GDQzZFg/j4vKU96yl2q6mcUkzKOgW4gwf1Z8cZToUCrz31XHlPWH8MVb1r2tFtdDtTGJ7g==", + "version": "5.3.6", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", + "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", "dev": true, "requires": { + "@jridgewell/trace-mapping": "^0.3.14", "jest-worker": "^27.4.5", "schema-utils": "^3.1.1", "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1", - "terser": "^5.7.2" + "terser": "^5.14.1" }, "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, "schema-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", @@ -17016,19 +19050,48 @@ "ajv": "^6.12.5", "ajv-keywords": "^3.5.2" } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true }, "to-regex-range": { @@ -17040,15 +19103,11 @@ "is-number": "^7.0.0" } }, - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true }, "trim-newlines": { "version": "3.0.1", @@ -17057,27 +19116,9 @@ "dev": true }, "true-case-path": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz", - "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==", - "dev": true, - "requires": { - "glob": "^7.1.2" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-2.2.1.tgz", + "integrity": "sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q==", "dev": true }, "type-check": { @@ -17095,6 +19136,22 @@ "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", "dev": true }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "ua-parser-js": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.34.tgz", + "integrity": "sha512-cJMeh/eOILyGu0ejgTKB95yKT3zOenSe9UGE3vj6WfiOwgGYnmATUsnDixMFvdU+rNMvWih83hrUP8VwhF9yXQ==", + "dev": true + }, "uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", @@ -17102,9 +19159,9 @@ "dev": true }, "underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", "dev": true }, "unicode-canonical-property-names-ecmascript": { @@ -17124,48 +19181,51 @@ } }, "unicode-match-property-value-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz", - "integrity": "sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", "dev": true }, "unicode-property-aliases-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz", - "integrity": "sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true }, - "union": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", - "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==", - "dev": true, - "requires": { - "qs": "^6.4.0" - } - }, "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", "dev": true, "requires": { - "unique-slug": "^2.0.0" + "unique-slug": "^3.0.0" } }, "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", "dev": true, "requires": { "imurmurhash": "^0.1.4" } }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, "update-browserslist-db": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", - "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", "dev": true, "requires": { "escalade": "^3.1.1", @@ -17181,12 +19241,6 @@ "punycode": "^2.1.0" } }, - "url-join": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true - }, "url-loader": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", @@ -17214,13 +19268,13 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "dev": true }, "v8-compile-cache": { @@ -17239,16 +19293,17 @@ "spdx-expression-parse": "^3.0.0" } }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true }, "watchpack": { "version": "2.4.0", @@ -17261,9 +19316,9 @@ } }, "webpack": { - "version": "5.74.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", - "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.3", @@ -17292,6 +19347,12 @@ "webpack-sources": "^3.2.3" }, "dependencies": { + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, "eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -17322,23 +19383,32 @@ } }, "webpack-cli": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", - "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", + "integrity": "sha512-S3KVAyfwUqr0Mo/ur3NzIp6jnerNpo7GUO6so51mxLi1spqsA17YcMXy0WOIJtBSnj748lthxC6XLbNKh/ZC+A==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.2.0", - "@webpack-cli/info": "^1.5.0", - "@webpack-cli/serve": "^1.7.0", + "@webpack-cli/configtest": "^2.0.1", + "@webpack-cli/info": "^2.0.1", + "@webpack-cli/serve": "^2.0.1", "colorette": "^2.0.14", - "commander": "^7.0.0", + "commander": "^9.4.1", "cross-spawn": "^7.0.3", + "envinfo": "^7.7.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", - "interpret": "^2.2.0", - "rechoir": "^0.7.0", + "interpret": "^3.1.1", + "rechoir": "^0.8.0", "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true + } } }, "webpack-merge": { @@ -17357,15 +19427,6 @@ "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true }, - "whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "requires": { - "iconv-lite": "0.6.3" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -17436,19 +19497,26 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, "write-file-atomic": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", - "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" } }, + "ws": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==", + "dev": true, + "requires": {} + }, "xmlcreate": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.4.tgz", @@ -17462,9 +19530,9 @@ "dev": true }, "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yaml": { @@ -17474,26 +19542,18 @@ "dev": true }, "yargs": { - "version": "17.4.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.0.tgz", - "integrity": "sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, "requires": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.3", + "string-width": "^4.2.0", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "dependencies": { - "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", - "dev": true - } + "yargs-parser": "^20.2.2" } }, "yargs-parser": { diff --git a/package.json b/package.json index 87ed75a..db538da 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,16 @@ { "name": "beercrackerz", - "version": "0.0.2", + "version": "0.1.0", "description": "The website for beer lover, to share the best spot to crack a beer, or to easily refill this beverage of the gods!", "keywords": [], - "main": "src/BeerCrackerz.js", + "main": "front/src/BeerCrackerz.js", "scripts": { - "build": "webpack --config webpack/webpack.prod.js", - "watch": "webpack --watch --config webpack/webpack.dev.js", - "server": "http-server -p 1337 -c-1 -o index.html", - "doc": "jsdoc -c doc/jsDoc.json", - "beforecommit": "npm run doc && npm run build" + "build": "webpack --config front/webpack/webpack.prod.js", + "watch": "webpack --watch --config front/webpack/webpack.dev.js", + "doc": "jsdoc -c front/webpack/jsDoc.json", + "test": "karma start front/test/karma.config.js", + "test:dev": "karma start front/test/karma.config.js --dev=true", + "beforecommit": "npm run doc && npm run test && npm run build" }, "homepage": "https://github.com/MesseBasseProduction/BeerCrackerz/README.md", "repository": { @@ -19,32 +20,47 @@ "bugs": { "url": "https://github.com/MesseBasseProduction/BeerCrackerz/issues" }, - "author": "ArthurBeaulieu", + "author": "The BeerCrackerz Team", + "contributors": [ + { + "name": "Arthur Beaulieu" + }, + { + "name": "Raphaël Beekmann" + } + ], "license": "GPL-3.0", "devDependencies": { - "@babel/core": "^7.18.10", - "@babel/preset-env": "^7.18.10", - "babel-loader": "^8.2.5", + "@babel/core": "^7.21.0", + "@babel/preset-env": "^7.20.2", + "babel-loader": "^9.1.2", "clean-webpack-plugin": "^4.0.0", - "css-loader": "^6.7.1", - "cssnano": "^5.1.13", - "eslint": "^8.22.0", - "eslint-webpack-plugin": "^3.2.0", + "css-loader": "^6.7.3", + "cssnano": "^5.1.15", + "eslint": "^8.36.0", + "eslint-webpack-plugin": "^4.0.0", "file-loader": "^6.2.0", - "http-server": "^14.1.1", - "jsdoc": "^3.6.11", - "mini-css-extract-plugin": "^2.6.1", - "node-sass": "^7.0.1", - "postcss": "^8.4.16", - "postcss-import": "^14.1.0", - "postcss-loader": "^7.0.1", - "postcss-preset-env": "^7.8.0", - "sass-loader": "^13.0.2", - "stylelint": "^14.10.0", - "stylelint-webpack-plugin": "^3.3.0", + "jasmine": "^4.5.0", + "jasmine-core": "^4.5.0", + "jsdoc": "^4.0.2", + "karma": "^6.4.1", + "karma-firefox-launcher": "^2.1.2", + "karma-html2js-preprocessor": "~1.1", + "karma-jasmine": "^5.1.0", + "karma-jasmine-html-reporter": "^2.0.0", + "karma-webpack": "^5.0.0", + "mini-css-extract-plugin": "^2.7.3", + "node-sass": "^8.0.0", + "postcss": "^8.4.21", + "postcss-import": "^15.1.0", + "postcss-loader": "^7.0.2", + "postcss-preset-env": "^8.0.1", + "sass-loader": "^13.2.0", + "stylelint": "^15.2.0", + "stylelint-webpack-plugin": "^4.1.0", "url-loader": "^4.1.1", - "webpack": "^5.74.0", - "webpack-cli": "^4.10.0", + "webpack": "^5.76.1", + "webpack-cli": "^5.0.1", "webpack-merge": "^5.8.0" } } diff --git a/register.html b/register.html deleted file mode 100644 index 3390e06..0000000 --- a/register.html +++ /dev/null @@ -1,76 +0,0 @@ - - - - - Register | BeerCrackerz - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/BeerCrackerz.js b/src/BeerCrackerz.js deleted file mode 100644 index de785a8..0000000 --- a/src/BeerCrackerz.js +++ /dev/null @@ -1,1228 +0,0 @@ -import './BeerCrackerz.scss'; -import MapHelper from './js/MapHelper.js'; -import Providers from './js/utils/ProviderEnum.js'; -import ZoomSlider from './js/ui/ZoomSlider.js'; -import LangManager from './js/utils/LangManager.js'; -import Notification from './js/ui/Notification.js'; -import Rating from './js/ui/Rating.js'; -import Utils from './js/utils/Utils.js'; - - -/** - * @class - * @constructor - * @public - * @extends MapHelper -**/ -class BeerCrackerz extends MapHelper { - - - /** - * @summary The BeerCrackerz main component - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * This component handles the whole BeerCrackerz app. It includes the map manipulation, - * the geolocation API to update the user position and process any map events that are - * relevant to an UX stand point. For more information, please consult the application - * description page at https://about.beercrackerz.org/ - *
        - **/ - constructor() { - super(); - /** - * The core Leaflet.js map - * @type {Object} - * @private - **/ - this._map = null; - /** - * The zoom slider handler - * @type {Object} - * @private - **/ - this._zoomSlider = null; - /** - * The notification handler - * @type {Object} - * @private - **/ - this._notification = null; - /** - * The user object holds everything useful to ensure a proper session - * @type {Object} - * @private - **/ - this._user = { - lat: 48.853121540141096, // Default lat to Paris Notre-Dame latitude - lng: 2.3498955769881156, // Default lng to Paris Notre-Dame longitude - accuracy: 0, // Accuracy in meter given by geolocation API - marker: null, // The user marker on map - circle: null, // The accuracy circle around the user marker - range: null, // The range in which user can add a new marker - color: Utils.USER_COLOR, // The color to use for circle (match the user marker color) - id: -1, - username: '' - }; - /** - * The stored marks for spots, stores and bars - * @type {Object} - * @private - **/ - this._marks = { - spot: [], - store: [], - bar: [], - }; - /** - * The stored clusters for markers, see Leaflet.markercluster plugin - * @type {Object} - * @private - **/ - this._clusters = { - spot: {}, - store: {}, - bar: {}, - }; - /** - * The temporary marker for new marks only - * @type {Object} - * @private - **/ - this._newMarker = null; - /** - * The debug DOM object - * @type {Object} - * @private - **/ - this._debugElement = null; - /** - * ID for geolocation watch callback - * @type {Number} - * @private - **/ - this._watchId = null; - /** - * Flag to know if a zoom action is occuring on map - * @type {Boolean} - * @private - **/ - this._isZooming = false; - /** - * The LangManager must be instantiated to handle nls accross the app - * @type {Boolean} - * @private - **/ - // The BeerCrackerz app is only initialized once nls are set up - this._lang = new LangManager( - window.navigator.language.substring(0, 2), - this._init.bind(this) - ); - } - - - // ======================================================================== // - // ----------------- Application initialization sequence ------------------ // - // ======================================================================== // - - - /** - * @method - * @name _init - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _init() method is designed to properly configure the user session, according - * to its saved preferences and its position. It first build the debug interface, - * then loads the user preferences, then create the map and finally, events are listened. - *
        - **/ - _init() { - this._debugElement = Utils.initDebugInterface(); - this._notification = new Notification(); - this._initUser() - .then(this._initPreferences.bind(this)) - .then(this._initGeolocation.bind(this)) - .then(this._initMap.bind(this)) - .then(this._initEvents.bind(this)) - .then(this._initMarkers.bind(this)); - } - - - /** - * @method - * @name _initUser - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _init() method initialize the user object according to its information - * and statistic so the UI can be properly built. - *
        - * @returns {Promise} A Promise resolved when preferences are set - **/ - _initUser() { - return new Promise(resolve => { - // TODO fill user information from server - this._user.id = 42; - this._user.username = 'messmaker'; - resolve(); - }); - } - - - /** - * @method - * @name _initPreferences - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _initPreferences() will initialize user preference if they are not set yet, - * it will also update the UI according to user preferences ; debug DOM visible, - * update the command classList for selected ones. - *
        - * @returns {Promise} A Promise resolved when preferences are set - **/ - _initPreferences() { - return new Promise(resolve => { - if (Utils.getPreference('poi-show-spot') === null) { - Utils.setPreference('poi-show-spot', true); - } - - if (Utils.getPreference('poi-show-store') === null) { - Utils.setPreference('poi-show-store', true); - } - - if (Utils.getPreference('poi-show-bar') === null) { - Utils.setPreference('poi-show-bar', true); - } - - if (Utils.getPreference('map-plan-layer') === null) { - Utils.setPreference('map-plan-layer', true); - } - - if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) { - window.DEBUG = true; // Ensure to set global flag if preference comes from local storage - Utils.setPreference('app-debug', true); // Ensure to set local storage preference if debug flag was added to the url - this.addDebugUI(); - } - // Update icon class if center on preference is set to true - if (Utils.getPreference('map-center-on-user') === 'true') { - document.getElementById('center-on').classList.add('lock-center-on'); - } - - resolve(); - }); - } - - - /** - * @method - * @name _initGeolocation - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _initGeolocation() method will request from browser the location authorization. - * Once granted, an event listener is set on any position update, so it can update the - * map state and the markers position. This method can be called again, only if the - * geolocation watch has been cleared ; for example when updating the accuracy options. - *
        - * @returns {Promise} A Promise resolved when preferences are set - **/ - _initGeolocation() { - return new Promise(resolve => { - if ('geolocation' in navigator) { - const options = (Utils.getPreference('map-high-accuracy') === 'true') ? Utils.HIGH_ACCURACY : Utils.OPTIMIZED_ACCURACY; - this._watchId = navigator.geolocation.watchPosition(position => { - // Update saved user position - this._user.lat = position.coords.latitude; - this._user.lng = position.coords.longitude; - this._user.accuracy = position.coords.accuracy; - // Only draw marker if map is already created - if (this._map) { - this.drawUserMarker(); - this.updateMarkerCirclesVisibility(); - // Update map position if focus lock is active - if (Utils.getPreference('map-center-on-user') === 'true' && !this._isZooming) { - this._map.setView(this._user); - } - // Updating debug info - this.updateDebugUI(); - } - resolve(); - }, resolve, options); - } else { - this._notification.raise(this.nls.notif('geolocationError')); - resolve(); - } - }); - } - - - /** - * @method - * @name _initMap - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _initMap() method will create the Leaflet.js map with two base layers (plan/satellite), - * add scale control, remove zoom control and set map bounds. - *
        - * @returns {Promise} A Promise resolved when preferences are set - **/ - _initMap() { - return new Promise(resolve => { - // Use main div to inject OSM into - this._map = window.L.map('beer-crakerz-map', { - zoomControl: false, - }).setView([this._user.lat, this._user.lng], 18); - // Add meter and feet scale on map - window.L.control.scale().addTo(this._map); - // Place user marker on the map - this.drawUserMarker(); - // Add OSM credits to the map next to leaflet credits - const osm = Providers.planOsm; - //const plan = Providers.planGeo; - const esri = Providers.satEsri; - //const geo = Providers.satGeo; - // Prevent panning outside of the world's edge - this._map.setMaxBounds(Utils.MAP_BOUNDS); - // Add layer group to interface - const baseMaps = {}; - baseMaps[`

        ${this.nls.map('planLayerOSM')}

        `] = osm; - //baseMaps[`

        ${this.nls.map('planLayerGeo')}

        `] = plan; - baseMaps[`

        ${this.nls.map('satLayerEsri')}

        `] = esri; - //baseMaps[`

        ${this.nls.map('satLayerGeo')}

        `] = geo; - // Append layer depending on user preference - if (Utils.getPreference('map-plan-layer')) { - switch (Utils.getPreference('map-plan-layer')) { - case this.nls.map('planLayerOSM'): - osm.addTo(this._map); - break; - /*case this.nls.map('planLayerGeo'): - plan.addTo(this._map); - break;*/ - case this.nls.map('satLayerEsri'): - esri.addTo(this._map); - break; - /*case this.nls.map('satLayerGeo'): - geo.addTo(this._map); - break;*/ - default: - osm.addTo(this._map); - break; - } - } else { // No saved pref, fallback on OSM base map - osm.addTo(this._map); - } - // Add layer switch radio on bottom right of the map - window.L.control.layers(baseMaps, {}, { position: 'bottomright' }).addTo(this._map); - // Init zoom slider when map has been created - this._zoomSlider = new ZoomSlider(this._map); - resolve(); - }); - } - - - /** - * @method - * @name _initEvents - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _initEvents() method will listen to all required events to manipulate the map. Those events - * are both for commands and for map events (click, drag, zoom and layer change). - *
        - * @returns {Promise} A Promise resolved when preferences are set - **/ - _initEvents() { - return new Promise(resolve => { - // Command events - document.getElementById('user-profile').addEventListener('click', this.userProfileModal.bind(this)); - document.getElementById('hide-show').addEventListener('click', this.hidShowModal.bind(this)); - document.getElementById('center-on').addEventListener('click', this.toggleFocusLock.bind(this)); - document.getElementById('overlay').addEventListener('click', this.closeModal.bind(this)); - // Subscribe to click event on map to react - this._map.on('click', this.mapClicked.bind(this)); - // Map is dragged by user mouse/finger - this._map.on('drag', () => { - // Constrain pan to the map bounds - this._map.panInsideBounds(Utils.MAP_BOUNDS, { animate: true }); - // Disable lock focus if user drags the map - if (Utils.getPreference('map-center-on-user') === 'true') { - this.toggleFocusLock(); - } - }); - // Map events - this._map.on('zoomstart', () => { - this._isZooming = true; - if (Utils.getPreference('poi-show-circle') === 'true') { - this.setMarkerCircles(this._marks.spot, false); - this.setMarkerCircles(this._marks.store, false); - this.setMarkerCircles(this._marks.bar, false); - this.setMarkerCircles([this._user], false); - this.setMarkerCircles([{ circle: this._user.range }], false); - } - }); - this._map.on('zoomend', () => { - this._isZooming = false; - if (Utils.getPreference('poi-show-circle') === 'true') { - if (this._map.getZoom() >= 15) { - this.setMarkerCircles(this._marks.spot, true); - this.setMarkerCircles(this._marks.store, true); - this.setMarkerCircles(this._marks.bar, true); - this.setMarkerCircles([this._user], true); - this.setMarkerCircles([{ circle: this._user.range }], true); - } - } - // Auto hide labels if zoom level is too high (and restore it when needed) - if (Utils.getPreference('poi-marker-label') === 'true') { - if (this._map.getZoom() < 15) { - this.setMarkerLabels(this._marks.spot, false); - this.setMarkerLabels(this._marks.store, false); - this.setMarkerLabels(this._marks.bar, false); - } else { - this.setMarkerLabels(this._marks.spot, true); - this.setMarkerLabels(this._marks.store, true); - this.setMarkerLabels(this._marks.bar, true); - } - } - // Updating debug info - this.updateDebugUI(); - }); - this._map.on('baselayerchange', event => { - Utils.setPreference('map-plan-layer', Utils.stripDom(event.name)); - }); - resolve(); - }); - } - - - /** - * @method - * @name _initMarkers - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _initEvents() method will initialize all saved marker into the map. - * Markers must be retrieved from server with a specific format to ensure it works - *
        - * @returns {Promise} A Promise resolved when preferences are set - **/ - _initMarkers() { - return new Promise(resolve => { - // Init map clusters for marks to be displayed (disable clustering at opened popup zoom level) - const clusterOptions = { - animateAddingMarkers: true, - disableClusteringAtZoom: 18, - spiderfyOnMaxZoom: false - }; - this._clusters.spot = new window.L.MarkerClusterGroup(Object.assign(clusterOptions, { - iconCreateFunction: cluster => { - return window.L.divIcon({ - className: 'cluster-icon-wrapper', - html: ` - - ${cluster.getChildCount()} - ` - }); - } - })); - this._clusters.store = new window.L.MarkerClusterGroup(Object.assign(clusterOptions, { - iconCreateFunction: cluster => { - return window.L.divIcon({ - className: 'cluster-icon-wrapper', - html: ` - - ${cluster.getChildCount()} - ` - }); - } - })); - this._clusters.bar = new window.L.MarkerClusterGroup(Object.assign(clusterOptions, { - iconCreateFunction: cluster => { - return window.L.divIcon({ - className: 'cluster-icon-wrapper', - html: ` - - ${cluster.getChildCount()} - ` - }); - } - })); - // Append clusters to the map depending on user preferences - if (Utils.getPreference(`poi-show-spot`) === 'true') { - this._map.addLayer(this._clusters.spot); - } - if (Utils.getPreference(`poi-show-store`) === 'true') { - this._map.addLayer(this._clusters.store); - } - if (Utils.getPreference(`poi-show-bar`) === 'true') { - this._map.addLayer(this._clusters.bar); - } - // Load data from local storage, later to be fetched from server - const iterateMarkers = mark => { - this.markPopupFactory(mark).then(dom => { - mark.dom = dom; - mark.marker = this.placeMarker(mark); - this._marks[mark.type].push(mark); - this._clusters[mark.type].addLayer(mark.marker); - }); - }; - let marks = JSON.parse(Utils.getPreference('saved-spot')) || []; - for (let i = 0; i < marks.length; ++i) { - iterateMarkers(marks[i]); - } - marks = JSON.parse(Utils.getPreference('saved-store')) || []; - for (let i = 0; i < marks.length; ++i) { - iterateMarkers(marks[i]); - } - marks = JSON.parse(Utils.getPreference('saved-bar')) || []; - for (let i = 0; i < marks.length; ++i) { - iterateMarkers(marks[i]); - } - - resolve(); - }); - } - - - // ======================================================================== // - // ------------------------- Toggle for map items ------------------------- // - // ======================================================================== // - - - /** - * @method - * @name toggleFocusLock - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The toggleFocusLock() method will, depending on user preference, lock or unlock - * the map centering around the user marker at each position refresh. This way the user - * can roam while the map is following its position. - *
        - **/ - toggleFocusLock() { - if (Utils.getPreference('map-center-on-user') === 'true') { - this._notification.raise(this.nls.notif(`unlockFocusOn`)); - document.getElementById('center-on').classList.remove('lock-center-on'); - Utils.setPreference('map-center-on-user', 'false'); - } else { - this._notification.raise(this.nls.notif(`lockFocusOn`)); - document.getElementById('center-on').classList.add('lock-center-on'); - this._map.flyTo([this._user.lat, this._user.lng], 18); - Utils.setPreference('map-center-on-user', 'true'); - } - } - - - /** - * @method - * @name toggleLabel - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The toggleLabel() method will, depending on user preference, display or not - * the labels attached to spots/stores/bars marks. This label is basically the - * mark name given by its creator. - *
        - **/ - toggleLabel() { - const visible = !(Utils.getPreference('poi-marker-label') === 'true'); - this.setMarkerLabels(this._marks.spot, visible); - this.setMarkerLabels(this._marks.store, visible); - this.setMarkerLabels(this._marks.bar, visible); - Utils.setPreference('poi-marker-label', visible); - } - - - /** - * @method - * @name toggleCircle - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The toggleCircle() method will, depending on user preference, display or not - * the circles around the spots/stores/bars marks. This circle indicates the minimal - * distance which allow the user to make updates on the mark information - *
        - **/ - toggleCircle() { - const visible = !(Utils.getPreference('poi-show-circle') === 'true'); - this.setMarkerCircles(this._marks.spot, visible); - this.setMarkerCircles(this._marks.store, visible); - this.setMarkerCircles(this._marks.bar, visible); - this.setMarkerCircles([this._user], visible); - this.setMarkerCircles([{ circle: this._user.range }], visible); - Utils.setPreference('poi-show-circle', visible); - } - - - /** - * @method - * @name toggleMarkers - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The toggleMarkers() method will, depending on user preference, display or not - * a given mark type. This way, the user can fine tune what is displayed on the map. - * A mark type in spots/stores/bars must be given as an argument - *
        - * @param {String} type The mark type in spots/tores/bars - **/ - toggleMarkers(type) { - const visible = !(Utils.getPreference(`poi-show-${type}`) === 'true'); - if (visible === true) { - this._map.addLayer(this._clusters[type]); - } else { - this._map.removeLayer(this._clusters[type]); - } - Utils.setPreference(`poi-show-${type}`, visible); - } - - - /** - * @method - * @name toggleHighAccuracy - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The toggleHighAccuracy() method will, depending on user preference, update the - * geolocation accuracy between optimized and high. The high settings might cause - * more memory and processing consumption, but gives better results. It will clear - * any previous position watch on the geolocation API so it can subscribe a new one - * with the new accuracy parameters (see Utils for values) - *
        - **/ - toggleHighAccuracy() { - const high = !(Utils.getPreference('map-high-accuracy') === 'true'); - Utils.setPreference('map-high-accuracy', high); - navigator.geolocation.clearWatch(this._watchId); - this._initGeolocation().then(this.updateDebugUI.bind(this)); - } - - - /** - * @method - * @name toggleDebug - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The toggleDebug() method will, depending on user preference, add or remove - * the debug DOM element to the user interface. The debug DOM display several - * useful information to identify an issue with the geolocation API - *
        - **/ - toggleDebug() { - const visible = !window.DEBUG; - window.DEBUG = visible; - Utils.setPreference('app-debug', visible); - if (visible) { - this.addDebugUI(); - } else { - this.removeDebugUI(); - } - } - - - // ======================================================================== // - // ----------------- App modals display and interaction ------------------- // - // ======================================================================== // - - - newMarkModal(dom) { - document.getElementById('overlay').appendChild(dom); - document.getElementById('overlay').style.display = 'flex'; - setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50); - } - - - editMarkModal(options) { - Utils.fetchTemplate(`assets/html/modal/edit${options.type}.html`).then(dom => { - const name = dom.querySelector(`#${options.type}-name`); - const description = dom.querySelector(`#${options.type}-desc`); - const submit = dom.querySelector(`#${options.type}-submit`); - const cancel = dom.querySelector(`#${options.type}-cancel`); - const rate = dom.querySelector(`#${options.type}-rating`); - const rating = new Rating(rate, options.rate); - // Update nls for template - Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal(`${options.type}EditTitle`)); - Utils.replaceString(dom.querySelector(`#nls-${options.type}-name`), `{{${options.type.toUpperCase()}_NAME}}`, this.nls[options.type]('nameLabel')); - Utils.replaceString(dom.querySelector(`#nls-${options.type}-desc`), `{{${options.type.toUpperCase()}_DESC}}`, this.nls[options.type]('descLabel')); - Utils.replaceString(dom.querySelector(`#nls-${options.type}-rate`), `{{${options.type.toUpperCase()}_RATE}}`, this.nls[options.type]('rateLabel')); - Utils.replaceString(submit, `{{${options.type.toUpperCase()}_SUBMIT}}`, this.nls.nav('add')); - Utils.replaceString(cancel, `{{${options.type.toUpperCase()}_CANCEL}}`, this.nls.nav('cancel')); - name.value = options.name; - description.value = options.description; - submit.addEventListener('click', () => { - // Iterate through marks to find matching one (by coord as marks coordinates are unique) - for (let i = 0; i < this._marks[options.type].length; ++i) { - // We found, remove circle, label and marker from map/clusters - if (options.lat === this._marks[options.type][i].lat && options.lng === this._marks[options.type][i].lng) { - this._marks[options.type][i].name = name.value; - this._marks[options.type][i].description = description.value; - this._marks[options.type][i].rate = rating.currentRate; - options.tooltip.removeFrom(this.map); - this.markPopupFactory(options).then(dom => { - options.dom = dom; - options.marker.setPopupContent(options.dom); - }); - break; - } - } - // Format marks to be saved and then update user preference with - const formattedMarks = []; - for (let i = 0; i < this._marks[options.type].length; ++i) { - formattedMarks.push(this.formatSavedMarker(this._marks[options.type][i])); - } - Utils.setPreference(`saved-${options.type}`, JSON.stringify(formattedMarks)); - // Notify user through UI that marker has been successfully deleted - this._notification.raise(this.nls.notif(`${options.type}Deleted`)); - this.closeModal(null, true); - }); - - cancel.addEventListener('click', this.closeModal.bind(this, null, true)); - document.getElementById('overlay').appendChild(dom); - document.getElementById('overlay').style.display = 'flex'; - setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50); - }); - } - - - /** - * @method - * @name deleteMarkModal - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since February 2022 - * @description - *
        - * The deleteMarkModal() method will request the mark delete modal, which prompts - * the user a confirmation to actually delete the mark - *
        - * @param {Function} cb The function to callback with true or false depending on user's choice - **/ - deleteMarkModal(cb) { - Utils.fetchTemplate('assets/html/modal/deletemark.html').then(dom => { - // Update nls for template - Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('deleteMarkTitle')); - Utils.replaceString(dom.querySelector(`#nls-modal-desc`), `{{MODAL_DESC}}`, this.nls.modal('deleteMarkDesc')); - Utils.replaceString(dom.querySelector(`#cancel-close`), `{{MODAL_CANCEL}}`, this.nls.nav('cancel')); - Utils.replaceString(dom.querySelector(`#delete-close`), `{{MODAL_DELETE}}`, this.nls.nav('delete')); - document.getElementById('overlay').appendChild(dom); - document.getElementById('overlay').style.display = 'flex'; - // Setup callback for confirm/cancel buttons - document.getElementById('cancel-close').addEventListener('click', e => { - this.closeModal(e); - cb(false); - }, false); - document.getElementById('delete-close').addEventListener('click', e => { - this.closeModal(e); - cb(true); - }, false); - setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50); - }); - } - - - /** - * @method - * @name userProfileModal - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The userProfileModal() method will request the user modal, which contains - * the user preferences, and the user profile information - *
        - **/ - userProfileModal() { - Utils.fetchTemplate('assets/html/modal/user.html').then(dom => { - // Update nls for template - Utils.replaceString(dom.querySelector(`#nls-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('userTitle')); - Utils.replaceString(dom.querySelector(`#nls-user-modal-accuracy`), `{{ACCURACY_USER_MODAL}}`, this.nls.modal('userAccuracyPref')); - Utils.replaceString(dom.querySelector(`#nls-user-modal-debug`), `{{DEBUG_USER_MODAL}}`, this.nls.modal('userDebugPref')); - Utils.replaceString(dom.querySelector(`#nls-about-desc`), `{{BEERCRACKERZ_DESC}}`, this.nls.modal('aboutDesc')); - - document.getElementById('overlay').appendChild(dom); - document.getElementById('overlay').style.display = 'flex'; - // Init modal checkbox state according to local storage preferences - if (Utils.getPreference('map-high-accuracy') === 'true') { - document.getElementById('high-accuracy-toggle').checked = true; - } - - if (window.DEBUG === true || (Utils.getPreference('app-debug') === 'true')) { - document.getElementById('debug-toggle').checked = true; - } - - document.getElementById('high-accuracy-toggle').addEventListener('change', this.toggleHighAccuracy.bind(this)); - document.getElementById('debug-toggle').addEventListener('change', this.toggleDebug.bind(this)); - - setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50); - }); - } - - - /** - * @method - * @name hidShowModal - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The hidShowModal() method will request the hide show modal, which all - * toggles for map elements ; labels/circles/spots/stores/bars - *
        - **/ - hidShowModal() { - Utils.fetchTemplate('assets/html/modal/hideshow.html').then(dom => { - // Update template nls - Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-title`), `{{MODAL_TITLE}}`, this.nls.modal('hideShowTitle')); - Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-labels`), `{{LABELS_HIDESHOW_MODAL}}`, this.nls.modal('hideShowLabels')); - Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-circles`), `{{CIRCLES_HIDESHOW_MODAL}}`, this.nls.modal('hideShowCircles')); - Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-spots`), `{{SPOTS_HIDESHOW_MODAL}}`, this.nls.modal('hideShowSpots')); - Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-stores`), `{{STORES_HIDESHOW_MODAL}}`, this.nls.modal('hideShowStores')); - Utils.replaceString(dom.querySelector(`#nls-hideshow-modal-bars`), `{{BARS_HIDESHOW_MODAL}}`, this.nls.modal('hideShowBars')); - Utils.replaceString(dom.querySelector(`#modal-close-button`), `{{MODAL_CLOSE}}`, this.nls.nav('close')); - document.getElementById('overlay').appendChild(dom); - document.getElementById('overlay').style.display = 'flex'; - // Init modal checkbox state according to local storage preferences - if (Utils.getPreference('poi-marker-label') === 'true') { - document.getElementById('label-toggle').checked = true; - } - - if (Utils.getPreference('poi-show-circle') === 'true') { - document.getElementById('circle-toggle').checked = true; - } - - if (Utils.getPreference('poi-show-spot') === 'true') { - document.getElementById('show-spots').checked = true; - } - - if (Utils.getPreference('poi-show-store') === 'true') { - document.getElementById('show-stores').checked = true; - } - - if (Utils.getPreference('poi-show-bar') === 'true') { - document.getElementById('show-bars').checked = true; - } - - document.getElementById('label-toggle').addEventListener('change', this.toggleLabel.bind(this)); - document.getElementById('circle-toggle').addEventListener('change', this.toggleCircle.bind(this)); - document.getElementById('show-spots').addEventListener('change', this.toggleMarkers.bind(this, 'spot')); - document.getElementById('show-stores').addEventListener('change', this.toggleMarkers.bind(this, 'store')); - document.getElementById('show-bars').addEventListener('change', this.toggleMarkers.bind(this, 'bar')); - - setTimeout(() => document.getElementById('overlay').style.opacity = 1, 50); - }); - } - - - /** - * @method - * @name closeModal - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The closeModal() method will close any opened modal if the click event is - * targeted on the modal overlay or on close buttons - *
        - * @param {Event} event The click event - **/ - closeModal(event, force) { - if (force === true || event.target.id === 'overlay' || event.target.id.indexOf('close') !== -1) { - document.getElementById('overlay').style.opacity = 0; - setTimeout(() => { - document.getElementById('overlay').style.display = 'none'; - document.getElementById('overlay').innerHTML = ''; - }, 300); - } - } - - - // ======================================================================== // - // -------------------------- Map interaction ----------------------------- // - // ======================================================================== // - - - /** - * @method - * @name mapClicked - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The mapClicked() method is the callback used when the user clicked on the Leaflet.js map - *
        - * @param {Event} event The click event - **/ - mapClicked(event) { - if (this._newMarker && this._newMarker.popupClosed) { - // Avoid to open new marker right after popup closing - this._newMarker = null; - } else if (this._newMarker === null || !this._newMarker.isBeingDefined) { - // Only create new marker if none is in progress, and that click is max range to add a marker - const distance = Utils.getDistanceBetweenCoords([this._user.lat, this._user.lng], [event.latlng.lat, event.latlng.lng]); - if (distance < Utils.NEW_MARKER_RANGE) { - this._newMarker = this.definePOI(event.latlng, this._markerSaved.bind(this)); - } else { - this._notification.raise(this.nls.notif('newMarkerOutside')); - } - } - } - - - /** - * @method - * @name _markerSaved - * @private - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The _markerSaved() method is the callback used when a marker is created and added - * to the map. It is the last method of a new marker proccess. - *
        - * @param {Object} options The new marker options - **/ - _markerSaved(options) { - // Save marke in marks and clusters for the map - this._marks[options.type].push(options); - this._clusters[options.type].addLayer(options.marker); - // Notify user that new marker has been saved - this._notification.raise(this.nls.notif(`${options.type}Added`)); - // Update marker circles visibility according to user position - this.updateMarkerCirclesVisibility(); - // Clear new marker to let user add other stuff - this._newMarker = null; - // Save new marker in local storage, later to be sent to the server - const marks = JSON.parse(Utils.getPreference(`saved-${options.type}`)) || []; - marks.push(this.formatSavedMarker(options)); - Utils.setPreference(`saved-${options.type}`, JSON.stringify(marks)); - } - - - /** - * @method - * @name updateMarkerCirclesVisibility - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The updateMarkerCirclesVisibility() method will update the circle visibility for - * all mark types (spots/stores/bars) and for the user marker - *
        - **/ - updateMarkerCirclesVisibility() { - const _updateByType = data => { - // Check spots in user's proximity - for (let i = 0; i < data.length; ++i) { - // Only update circles that are in user view - if (this._map.getBounds().contains(data[i].marker.getLatLng())) { - const marker = data[i].marker; - const distance = Utils.getDistanceBetweenCoords([this._user.lat, this._user.lng], [marker.getLatLng().lat, marker.getLatLng().lng]); - // Only show if user distance to marker is under circle radius - if (distance < Utils.CIRCLE_RADIUS && !data[i].circle.visible) { - data[i].circle.visible = true; - data[i].circle.setStyle({ - opacity: 1, - fillOpacity: 0.1 - }); - } else if (distance >= Utils.CIRCLE_RADIUS && data[i].circle.visible) { - data[i].circle.visible = false; - data[i].circle.setStyle({ - opacity: 0, - fillOpacity: 0 - }); - } - } - } - }; - - if (Utils.getPreference('poi-show-circle') === 'true') { - _updateByType(this._marks.spot); - _updateByType(this._marks.store); - _updateByType(this._marks.bar); - _updateByType([this._user]); - } - } - - - // ======================================================================== // - // -------------------------- Marker edition ------------------------------ // - // ======================================================================== // - - - /** - * @method - * @name formatSavedMarker - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since February 2022 - * @description - *
        - * This method formats a mark returned from MapHelper so it can be parsed - * using JSON.parse (in order to store it in local storage/database) - *
        - * @param {Object} mark The mark options from internal this._marks[type] - **/ - formatSavedMarker(mark) { - return { - type: mark.type, - lat: mark.lat, - lng: mark.lng, - name: mark.name, - description: mark.description, - user: mark.username || this.user.username, - userId: mark.userId || this.user.id, - dom: null, - rate: mark.rate, - marker: null, - circle: null, - }; - } - - - /** - * @method - * @name editMarker - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since February 2022 - * @description - *
        - * This method will open a mark edition modal - *
        - * @param {Object} options The mark options to edit - **/ - editMarker(options) { - this._map.closePopup(); - this.editMarkModal(options); - } - - - /** - * @method - * @name deleteMarker - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since February 2022 - * @description - *
        - * This method will delete a mark after prompting the user if he trully wants to - *
        - * @param {Object} options The mark options to delete - **/ - deleteMarker(options) { - this.deleteMarkModal(confirm => { - if (confirm === true) { - // Iterate through marks to find matching one (by coord as marks coordinates are unique) - const marks = this._marks[options.type]; - for (let i = 0; i < marks.length; ++i) { - // We found, remove circle, label and marker from map/clusters - if (options.lat === marks[i].lat && options.lng === marks[i].lng) { - this.setMarkerCircles([ marks[i] ], false); - this.setMarkerLabels([marks[i]], false); - this._clusters[options.type].removeLayer(marks[i].marker); - marks.splice(i, 1); - break; - } - } - // Update internal marks array - this._marks[options.type] = marks; - // Format marks to be saved and then update user preference with - const formattedMarks = []; - for (let i = 0; i < this._marks[options.type].length; ++i) { - formattedMarks.push(this.formatSavedMarker(this._marks[options.type][i])); - } - Utils.setPreference(`saved-${options.type}`, JSON.stringify(formattedMarks)); - // Notify user through UI that marker has been successfully deleted - this._notification.raise(this.nls.notif(`${options.type}Deleted`)); - } - }); - } - - - // ======================================================================== // - // ---------------------------- Debug methods ----------------------------- // - // ======================================================================== // - - - /** - * @method - * @name addDebugUI - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The addDebugUI() method appends the debug DOM element to the document body - *
        - **/ - addDebugUI() { - document.body.appendChild(this._debugElement); - } - - - /** - * @method - * @name removeDebugUI - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The removeDebugUI() method remove the debug DOM element from the document body - *
        - **/ - removeDebugUI() { - document.body.removeChild(this._debugElement); - } - - - /** - * @method - * @name updateDebugUI - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since January 2022 - * @description - *
        - * The updateDebugUI() method will update informations held in the debug DOM - *
        - **/ - updateDebugUI() { - const options = (Utils.getPreference('map-high-accuracy') === 'true') ? Utils.HIGH_ACCURACY : Utils.OPTIMIZED_ACCURACY; - Utils.updateDebugInterface(this._debugElement, this._user, options); - } - - - /** - * @method - * @name downloadData - * @public - * @memberof BeerCrackerz - * @author Arthur Beaulieu - * @since August 2022 - * @description - *
        - * The downloadData() method will save to user disk the saved spots as a JSON file - *
        - **/ - downloadData() { - const dataString = `data:text/json;charset=utf-8,${encodeURIComponent(Utils.getPreference('saved-spot'))}`; - const link = document.createElement('A'); - link.setAttribute('href', dataString); - link.setAttribute('download', 'BeerCrackerzData.json'); - link.click(); - } - - - // ======================================================================== // - // ---------------------------- Class accessors --------------------------- // - // ======================================================================== // - - - /** - * @public - * @property {Object} map - * Leaflet.js map getter - **/ - get map() { - return this._map; - } - - - /** - * @public - * @property {Object} marks - * Leaflet.js marks that holds spot/store/bar marks as subkeys - **/ - get marks() { - return this._marks; - } - - - /** - * @public - * @property {Object} user - * The session user object - **/ - get user() { - return this._user; - } - - - /** - * @public - * @property {Object} nls - * The LangManager getter - **/ - get nls() { - return this._lang; - } - - -} - - -export default BeerCrackerz; diff --git a/src/BeerCrackerz.scss b/src/BeerCrackerz.scss deleted file mode 100644 index 6d686fe..0000000 --- a/src/BeerCrackerz.scss +++ /dev/null @@ -1,464 +0,0 @@ -@import 'scss/keyframes'; -@import 'scss/base'; - -main.beer-crakerz-map { - height: 100%; - width: 100%; - z-index: 10; - - .new-poi { - text-align: center; - - p { - font-size: 1.6rem; - font-weight: bold; - } - - button { - margin: 1.2rem 0; - } - } - - .marker-tooltip { - font-style: italic; - text-align: center; - } -} - -nav { - display: flex; - flex-direction: column; - position: absolute; - right: 1rem; - top: 1rem; - width: 4.8rem; - z-index: 20; - - img { - cursor: pointer; - border-radius: 50%; - box-shadow: 0px 0px 10px #424242; - width: 100%; - } - - .cmd-bar { - .cmd-wrapper { - align-items: center; - background: white; - background-clip: padding-box; - border: 2px solid rgba(0, 0, 0, .2); - border-radius: .5rem; - display: flex; - justify-content: center; - margin: 1rem 0; - padding: .2rem; - - &:first-child { - margin-top: 0; - } - - img { - box-shadow: inherit; - transition: all .2s; - - &:active, - &:focus, - &:hover { - border: solid 1px rgb(185, 185, 185); - border-radius: .5rem; - padding: .2rem; - - filter: invert(70%) sepia(67%) saturate(455%) hue-rotate(67deg) brightness(163%) contrast(85%); - } - - &[class$="-on"] { - filter: invert(53%) sepia(30%) saturate(1977%) hue-rotate(155deg) brightness(88%) contrast(102%); - } - } - - &.logo img { - padding: .1rem; - - &:active, - &:focus, - &:hover { - filter: inherit; - } - } - } - } -} - -.notification-wrapper { - align-items: center; - background-color: rgba(255, 255, 255, .8); - border-radius: .5rem; - box-shadow: 0 0 10px #424242; - display: flex; - flex-direction: column; - height: 3rem; - justify-content: center; - left: calc(50% - 15rem); - opacity: 0; - position: absolute; - top: -4rem; - width: 30rem; - z-index: 40; - - transition: top .3s, opacity .2s ease-out; - - &.opened { - opacity: 1; - top: 1rem; - } - - .notification-message { - font-style: italic; - margin: 0; - } -} - -.zoom-slider { - align-items: center; - background: white; - background-clip: padding-box; - border: 2px solid rgba(0, 0, 0, .2); - border-radius: .5rem; - box-shadow: 0 0 10px #424242; - display: flex; - flex-direction: column; - height: 20rem; - justify-content: center; - opacity: 0; - position: absolute; - right: -6rem; - top: calc(50% - 10rem); - width: 4.8rem; - z-index: 20; - - transition: right .3s, opacity .2s ease-out; - - &.opened { - opacity: 1; - right: 1rem; - } - - p { - cursor: pointer; - font-size: 1.6rem; - font-weight: bold; - margin: .5rem 0; - user-select: none; - - &:last-child { - margin-bottom: 0; - } - } - - .slider-wrapper { - border-radius: .5rem; - box-shadow: 0 0 10px #424242; - height: 80%; - overflow: hidden; - position: relative; - width: 20%; - - .slider-position { - background-color: lime; - bottom: 0; - position: absolute; - width: 100%; - - transition: height .2s; - } - } -} - -.overlay { - align-items: center; - background: rgba(255, 255, 255, .8); - display: none; - height: 100%; - justify-content: center; - position: absolute; - opacity: 0; - top: 0; - width: 100%; - z-index: 30; - - transition: opacity .3s; - - [class$="-modal"] { - background: rgba(255, 255, 255, .8); - border-radius: .5rem; - box-shadow: 0px 0px 10px #424242; - padding: 3rem 4rem; - position: relative; - text-align: center; - - h1 { - margin-bottom: 3rem; - } - - button { - margin-top: 3rem; - } - - .modal-close { - color: #999; - cursor: pointer; - font-size: 2.2rem; - font-weight: bold; - line-height: 1.4rem; /* Because the cross symbol doesn't fit font height */ - right: 1rem; - position: absolute; - top: 1rem; - } - - .button-wrapper { - align-items: center; - display: flex; - justify-content: center; - - button { - margin-left: 1rem; - margin-right: 1rem; - } - } - } - - .new-mark-modal, - .edit-mark-modal { - width: calc(100% - 2rem); - - h1, p { - text-align: center; - } - - .rating { - margin-bottom: 1.2rem; - - img { - cursor: pointer; - height: 2.5rem; - margin: 0 .25rem; - width: 2.5rem; - - &.active { - filter: invert(95%) sepia(39%) saturate(3136%) hue-rotate(323deg) brightness(96%) contrast(115%); - - &.selected { - filter: invert(75%) sepia(9%) saturate(4002%) hue-rotate(67deg) brightness(81%) contrast(83%); - } - } - } - } - } - - .hide-show-modal { - .item { - align-items: center; - display: flex; - justify-content: space-between; - margin: 1rem auto; - max-width: 15rem; - - img { - margin-right: 2rem; - height: 2rem; - } - - label { - margin-bottom: 0; - margin-right: 2rem; - } - - input { - margin: 0; - width: auto; - } - } - } - - .user-profile-modal { - height: calc(100% - 2rem); - width: calc(100% - 2rem); - - .item { - align-items: center; - display: flex; - justify-content: space-between; - margin: 1rem auto; - max-width: 25rem; - - img { - margin-right: 2rem; - height: 2rem; - } - - label { - margin-bottom: 0; - margin-right: 2rem; - } - - input { - margin: 0; - width: auto; - } - } - } -} - -.debug-container { - background-color: rgba(255, 255, 255, .8); - border-radius: .5rem; - color: black; - left: 1rem; - padding: 1rem; - position: absolute; - top: 1rem; - z-index: 20; - - p { - margin-bottom: .2rem; - } -} - -.popup { - h1, h2, p { - margin: 0; - text-align: center; - } - - h1 { - font-size: 2rem; - margin-bottom: 1.2rem; - } - - .rating { - align-items: center; - display: flex; - justify-content: center; - width: 100%; - - img { - height: 1.2rem; - - &.active { - filter: invert(95%) sepia(39%) saturate(3136%) hue-rotate(323deg) brightness(96%) contrast(115%); - } - } - - p { - font-style: inherit; - margin-left: .5rem; - } - } - - p { - font-style: italic; - } - - h2 { - font-size: 1.3rem; - font-weight: inherit; - margin-top: 1.2rem; - } - - footer { - align-items: center; - display: flex; - position: relative; - justify-content: space-between; - margin-top: 1.2rem; - - img { - cursor: pointer; - height: 2rem; - margin-left: .5rem; - } - } -} - -.cluster-icon-wrapper { - position: relative; - - .cluster-icon { - width: auto; - height: 50px; - line-height: 50px; - margin-left: -21px; - margin-top: -13px; - filter: drop-shadow(0 0 .25rem #424242); - transition: transform .2s, filter .2s; - - &:active, - &:focus, - &:hover { - filter: drop-shadow(0 0 .33rem #A8A8A8); - transform: scale(1.066); - } - } - - .cluster-label { - background-color: white; - border: solid black 1px; - border-radius: .75rem; - box-shadow: 0 0 10px #424242; - color: black; - font-size: 1.1rem; - font-weight: bold; - left: 15px; - padding: .1rem .4rem; - position: absolute; - top: -15px; - } -} -/* Needs to override some leaflet styles to match BeerCrackerz interface */ -.leaflet-control-layers.leaflet-control { - transition: all .2s; - - a { - margin-bottom: 0; - } -} - -.leaflet-control-layers-expanded { - padding: 2rem !important; - - label { - margin: 0; - - div { - align-items: center; - display: flex; - flex-direction: row-reverse; - justify-content: space-between; - - input { - width: auto; - } - - input, p { - margin: 0; - } - - p { - margin-right: 1rem; - } - } - } -} - -.leaflet-marker-icon.leaflet-zoom-animated.leaflet-interactive { - filter: drop-shadow(0 0 .25rem #424242); - transition: transform .2s, filter .2s; - - &:active, - &:focus, - &:hover { - filter: drop-shadow(0 0 .33rem #A8A8A8); - } -} - -@import 'scss/responsive'; diff --git a/src/BeerCrackerzAuth.js b/src/BeerCrackerzAuth.js deleted file mode 100644 index 70dff29..0000000 --- a/src/BeerCrackerzAuth.js +++ /dev/null @@ -1,157 +0,0 @@ -import './BeerCrackerzAuth.scss'; -import LangManager from './js/utils/LangManager.js'; -import Utils from './js/utils/Utils.js'; - - -class BeerCrackerzAuth { - - - constructor() { - let _init = () => {}; - if (document.body.className.includes('login')) { - _init = this._handleLogin.bind(this); - } else if (document.body.className.includes('register')) { - _init = this._handleRegister.bind(this); - } - // The BeerCrackerz app is only initialized once nls are set up - this._lang = new LangManager( - window.navigator.language.substring(0, 2), - _init.bind(this) - ); - } - - - _handleLogin() { - // Update page nls according to browser language - document.title = this.nls.login('headTitle'); - Utils.replaceString(document.body, '{{LOGIN_SUBTITLE}}', this.nls.login('subtitle')); - Utils.replaceString(document.body, '{{LOGIN_HIDDEN_ERROR}}', this.nls.login('hiddenError')); - Utils.replaceString(document.body, '{{LOGIN_USERNAME_LABEL}}', this.nls.login('username')); - Utils.replaceString(document.body, '{{LOGIN_USERNAME_PASSWORD}}', this.nls.login('password')); - Utils.replaceString(document.body, '{{LOGIN_BUTTON}}', this.nls.login('login')); - Utils.replaceString(document.body, '{{LOGIN_NOT_REGISTERED}}', this.nls.login('notRegistered')); - Utils.replaceString(document.body, '{{LOGIN_REGISTER}}', this.nls.login('register')); - - const error = document.getElementById('login-error'); - const username = document.getElementById('username'); - const password = document.getElementById('password'); - // useful login method for field check and server response check - const _frontFieldValidation = () => { - // Handling empty error cases - if (username.value === '' && password.value === '') { - error.classList.add('visible'); - error.innerHTML = this.nls.login('bothEmpty'); - username.classList.add('error'); - password.classList.add('error'); - return false; - } else if (username.value === '') { - error.classList.add('visible'); - error.innerHTML = this.nls.login('usernameEmpty'); - username.classList.add('error'); - return false; - } else if (password.value === '') { - error.classList.add('visible'); - error.innerHTML = this.nls.login('passwordEmpty'); - password.classList.add('error'); - return false; - } - return true; - }; - const _backValidation = response => { - // Check response and handle status codes - console.log(response); - // If all front and back tests are ok, redirect to auth - // If the user ma nually force redirection to authindex, - // the server should reject the request as the user is not authenticated - window.location = 'authindex.html'; - }; - // Submit click event - document.getElementById('login-submit').addEventListener('click', () => { - // Reset error css classes - error.classList.remove('visible'); - username.classList.remove('error'); - password.classList.remove('error'); - if (_frontFieldValidation()) { - Utils.postReq('/api/login/submit').then(_backValidation).catch(() => { - error.classList.add('visible'); - error.innerHTML = this.nls.login('serverError'); - }); - } - }, false); - } - - - _handleRegister() { - // Update page nls according to browser language - document.title = this.nls.register('headTitle'); - Utils.replaceString(document.body, '{{REGISTER_SUBTITLE}}', this.nls.register('subtitle')); - Utils.replaceString(document.body, '{{REGISTER_HIDDEN_ERROR}}', this.nls.register('hiddenError')); - Utils.replaceString(document.body, '{{REGISTER_USERNAME_LABEL}}', this.nls.register('username')); - Utils.replaceString(document.body, '{{REGISTER_USERNAME_PASSWORD_1}}', this.nls.register('password1')); - Utils.replaceString(document.body, '{{REGISTER_USERNAME_PASSWORD_2}}', this.nls.register('password2')); - Utils.replaceString(document.body, '{{REGISTER_BUTTON}}', this.nls.register('register')); - Utils.replaceString(document.body, '{{REGISTER_ALREADY_DONE}}', this.nls.register('notRegistered')); - Utils.replaceString(document.body, '{{REGISTER_LOGIN}}', this.nls.register('login')); - const error = document.getElementById('register-error'); - const username = document.getElementById('username'); - const password1 = document.getElementById('password1'); - const password2 = document.getElementById('password2'); - // useful login method for field check and server response check - const _frontFieldValidation = () => { - // Handling empty error cases - if (username.value === '' || password1.value === '' || password2.value === '') { - error.classList.add('visible'); - error.innerHTML = this.nls.register('fieldEmpty'); - if (username.value === '') { - username.classList.add('error'); - } - if (password1.value === '') { - password1.classList.add('error'); - } - if (password2.value === '') { - password2.classList.add('error'); - } - return false; - } else if (password1.value !== password2.value) { - error.classList.add('visible'); - error.innerHTML = this.nls.register('notMatchingPassword'); - password1.classList.add('error'); - password2.classList.add('error'); - return false; - } - return true; - }; - const _backValidation = (response) => { - // Check response and handle status codes - console.log(response); - // If all front and back tests are ok, redirect to auth - // If the user ma nually force redirection to authindex, - // the server should reject the request as the user is not authenticated - window.location = 'authindex.html'; - }; - // Submit click event - document.getElementById('register-submit').addEventListener('click', () => { - // Reset error css classes - error.classList.remove('visible'); - username.classList.remove('error'); - password1.classList.remove('error'); - password2.classList.remove('error'); - if (_frontFieldValidation()) { - Utils.postReq('/api/register/submit').then(_backValidation).catch(() => { - error.classList.add('visible'); - error.innerHTML = this.nls.register('serverError'); - }); - } - }, false); - } - - - get nls() { - return this._lang; - } - - -} - - -export default BeerCrackerzAuth; diff --git a/src/BeerCrackerzAuth.scss b/src/BeerCrackerzAuth.scss deleted file mode 100644 index 4804962..0000000 --- a/src/BeerCrackerzAuth.scss +++ /dev/null @@ -1,60 +0,0 @@ -@import 'scss/keyframes'; -@import 'scss/base'; - -body { - background: white; - height: 100%; - position: relative; - width: 100%; -} - -aside { - background-color: grey; - display: flex; - flex-direction: column; - height: 100%; - justify-content: space-between; - max-width: 40rem; - position: absolute; - right: 0; - text-align: center; - width: 100%; - - h1 { - margin-bottom: 0; - } - - header { - margin-top: 5rem; - } - - main { - margin: 0 4rem; - text-align: left; - - button { - margin: 2.9rem auto 1.2rem; - } - - p { - text-align: right; - } - - .login-error { - color: transparent; - font-style: italic; - font-weight: bold; - text-align: center; - - transition: color .2s; - - &.visible { - color: #ff5454; - } - } - } - - footer { - margin-bottom: 5rem; - } -} diff --git a/src/js/MapHelper.js b/src/js/MapHelper.js deleted file mode 100644 index f0a21d4..0000000 --- a/src/js/MapHelper.js +++ /dev/null @@ -1,305 +0,0 @@ -import Markers from './utils/MarkerEnum.js'; -import Rating from './ui/Rating.js'; -import Utils from './utils/Utils.js'; - - -class MapHelper { - - - constructor() { /* Mixin to be extended from the BeerCrackerz main class */ } - - - // ======================================================================== // - // --------------------------- Marker helpers ----------------------------- // - // ======================================================================== // - - - placeMarker(options) { - let icon = Markers.black; - if (options.type === 'store') { - icon = Markers.blue; - } else if (options.type === 'spot') { - icon = Markers.green; - } else if (options.type === 'bar') { - icon = Markers.red; - } else if (options.type === 'user') { - icon = Markers.user; - } - - const marker = window.L.marker([options.lat, options.lng], { icon: icon }).on('click', () => { - // Disable center on lock if previously set to true - if (Utils.getPreference('map-center-on-user') === 'true') { - this.toggleFocusLock(); - } - // Actual fly to the marker - this.map.flyTo([options.lat, options.lng], 18); - }); - - if (options.dom) { - marker.bindPopup(options.dom); - } - // All markers that are not spot/store/bar should be appended to the map - if (['spot', 'store', 'bar'].indexOf(options.type) === -1) { - marker.addTo(this.map); - } - - return marker; - } - - - drawUserMarker() { - if (!this.user.marker) { // Create user marker if not existing - this.user.type = 'user'; - this.user.marker = this.placeMarker(this.user); - // Append circle around marker for accuracy and range for new marker - this.user.radius = this.user.accuracy; - this.user.circle = this.drawCircle(this.user); - this.user.range = this.drawCircle({ - lat: this.user.lat, - lng: this.user.lng, - radius: Utils.NEW_MARKER_RANGE, - color: Utils.RANGE_COLOR - }); - // Update circle opacity if pref is at true - if (Utils.getPreference('poi-show-circle') === 'true') { - this.user.circle.setStyle({ - opacity: 1, - fillOpacity: 0.1 - }); - this.user.range.setStyle({ - opacity: 1, - fillOpacity: 0.1 - }); - } - // Callback on marker clicked to add marker on user position - this.user.marker.on('click', this.mapClicked.bind(this)); - } else { // Update user marker position, range, and accuracy circle - this.user.marker.setLatLng(this.user); - this.user.range.setLatLng(this.user); - this.user.circle.setLatLng(this.user); - this.user.circle.setRadius(this.user.accuracy); - } - } - - - definePOI(options, callback) { - const dom = { - wrapper: document.createElement('DIV'), - title: document.createElement('P'), - spot: document.createElement('BUTTON'), - store: document.createElement('BUTTON'), - bar: document.createElement('BUTTON'), - }; - // Update class and inner HTMl content according to user's nls - dom.wrapper.className = 'new-poi'; - dom.title.innerHTML = this.nls.map('newTitle'); - dom.spot.innerHTML = this.nls.map('newSpot'); - dom.store.innerHTML = this.nls.map('newStore'); - dom.bar.innerHTML = this.nls.map('newBar'); - // Atach data type to each button (to be used in clicked callback) - dom.spot.dataset.type = 'spot'; - dom.store.dataset.type = 'store'; - dom.bar.dataset.type = 'bar'; - // DOM chaining - dom.wrapper.appendChild(dom.title); - dom.wrapper.appendChild(dom.spot); - dom.wrapper.appendChild(dom.store); - dom.wrapper.appendChild(dom.bar); - // Update popup content with DOM elements - options.dom = dom.wrapper; - // Create temporary mark with wrapper content and open it to offer user the creation menu - const marker = this.placeMarker(options).openPopup(); - options.marker = marker; // Attach marker to option so it can be manipulated in clicked callbacks - options.addedCallback = callback; // Attach callback to be called when marker addition is done - // Callback on button clicked (to open modal and define a new mark) - const _prepareNewMark = e => { - marker.isBeingDefined = true; - marker.closePopup(); - this.defineMarkFactory(e.target.dataset.type, options); - }; - // Buttons click events - dom.spot.addEventListener('click', _prepareNewMark); - dom.store.addEventListener('click', _prepareNewMark); - dom.bar.addEventListener('click', _prepareNewMark); - // Listen to clicks outside of popup to close new mark - marker.on('popupclose', () => { - if (!marker.isBeingDefined) { - marker.popupClosed = true; - marker.removeFrom(this.map); - } - }); - - return marker; - } - - - // ======================================================================== // - // ---------------------- New mark in modal helper ------------------------ // - // ======================================================================== // - - - defineMarkFactory(type, options) { - Utils.fetchTemplate(`assets/html/modal/new${type}.html`).then(dom => { - const name = dom.querySelector(`#${type}-name`); - const description = dom.querySelector(`#${type}-desc`); - const rating = new Rating(dom.querySelector(`#${type}-rating`)); - const submit = dom.querySelector(`#${type}-submit`); - const cancel = dom.querySelector(`#${type}-cancel`); - const close = dom.querySelector('#modal-close'); - // Update nls for template - Utils.replaceString(dom.querySelector(`#nls-${type}-title`), `{{${type.toUpperCase()}_TITLE}}`, this.nls[type]('title')); - Utils.replaceString(dom.querySelector(`#nls-${type}-subtitle`), `{{${type.toUpperCase()}_SUBTITLE}}`, this.nls[type]('subtitle')); - Utils.replaceString(dom.querySelector(`#nls-${type}-name`), `{{${type.toUpperCase()}_NAME}}`, this.nls[type]('nameLabel')); - Utils.replaceString(dom.querySelector(`#nls-${type}-desc`), `{{${type.toUpperCase()}_DESC}}`, this.nls[type]('descLabel')); - Utils.replaceString(dom.querySelector(`#nls-${type}-rate`), `{{${type.toUpperCase()}_RATE}}`, this.nls[type]('rateLabel')); - Utils.replaceString(submit, `{{${type.toUpperCase()}_SUBMIT}}`, this.nls.nav('add')); - Utils.replaceString(cancel, `{{${type.toUpperCase()}_CANCEL}}`, this.nls.nav('cancel')); - // Method to clear modal and hide it, and remove temporary marker on the map - const _cleanDefineUI = () => { - options.marker.isBeingDefined = false; - options.marker.removeFrom(this.map); // Clear temporary black marker - this.closeModal(null, true); - }; - // Submit or cancel event subscriptions - submit.addEventListener('click', () => { - if (name.value === '') { - this._notification.raise(this.nls.notif('markNameEmpty')); - } else { - _cleanDefineUI(); - options.type = type; - options.name = name.value, - options.description = description.value; - options.rate = rating.currentRate; - this.markPopupFactory(options).then(dom => { - options.dom = dom; - options.marker = this.placeMarker(options); // Create final marker - options.addedCallback(options); - }); - } - }); - cancel.addEventListener('click', _cleanDefineUI); - close.addEventListener('click', _cleanDefineUI); - this.newMarkModal(dom); - }); - } - - - defineNewSpot(options) { - this.defineMarkFactory('spot', options); - } - - - defineNewStore(options) { - this.defineMarkFactory('store', options); - } - - - defineNewBar(options) { - this.defineMarkFactory('bar', options); - } - - - // ======================================================================== // - // ------------------------- Mark popup helper ---------------------------- // - // ======================================================================== // - - - markPopupFactory(options) { - return new Promise(resolve => { - Utils.fetchTemplate(`assets/html/popup/${options.type}.html`).then(dom => { - const element = document.createElement('DIV'); - element.appendChild(dom); - const user = options.user || this.user.username; - const desc = Utils.stripDom(options.description) || this.nls.popup(`${options.type}NoDesc`); - Utils.replaceString(element, `{{${options.type.toUpperCase()}_NAME}}`, Utils.stripDom(options.name)); - Utils.replaceString(element, `{{${options.type.toUpperCase()}_FINDER}}`, user); - Utils.replaceString(element, `{{${options.type.toUpperCase()}_RATE}}`, options.rate + 1); - Utils.replaceString(element, `{{${options.type.toUpperCase()}_DESC}}`, desc); - Utils.replaceString(element, `{{${options.type.toUpperCase()}_FOUND_BY}}`, this.nls.popup(`${options.type}FoundBy`)); - // Fill mark rate (rating is in [0, 4] explaining the +1 in loop bound) - const rate = element.querySelector(`#${options.type}-rating`); - for (let i = 0; i < options.rate + 1; ++i) { - rate.children[i].classList.add('active'); - } - // Remove picture icon if user is not in range - const distance = Utils.getDistanceBetweenCoords([this.user.lat, this.user.lng], [options.lat, options.lng]); - if (distance > Utils.CIRCLE_RADIUS) { - console.log('Too far'); - //element.removeChild(element.querySelector('')); - } - // Remove edition buttons if marker is not user's one, this does not replace a server test for edition... - if (user !== this.user.username) { - element.removeChild(element.querySelector('#popup-edit')); - } else { - element.querySelector('#edit-mark').addEventListener('click', this.editMarker.bind(this, options), false); - element.querySelector('#delete-mark').addEventListener('click', this.deleteMarker.bind(this, options), false); - } - - // Append circle around marker - options.color = Utils[`${options.type.toUpperCase()}_COLOR`]; - options.circle = this.drawCircle(options); - // Create label for new marker - options.tooltip = window.L.tooltip({ - permanent: true, - direction: 'center', - className: 'marker-tooltip', - interactive: true - }).setContent(options.name) - .setLatLng(options.circle.getLatLng()); - // Only make it visible if preference is to true - if (Utils.getPreference('poi-marker-label') === 'true') { - options.tooltip.addTo(this.map); - } - // Send back the popup - resolve(element); - }); - }); - } - - - drawCircle(options) { - return window.L.circle(options, { - color: options.color, - fillColor: options.color, - opacity: 0, // This needs to be updated according to user proximity - fillOpacity: 0, // Same for this parameter - radius: options.radius ? options.radius : Utils.CIRCLE_RADIUS, - }).addTo(this.map); - } - - - setMarkerCircles(marks, visible) { - for (let i = 0; i < marks.length; ++i) { - // Here we update both opacity and add/remove circle from map - if (visible) { - marks[i].circle.setStyle({ - opacity: 1, - fillOpacity: 0.1 - }); - marks[i].circle.addTo(this.map); - } else { - marks[i].circle.setStyle({ - opacity: 0, - fillOpacity: 0 - }); - marks[i].circle.removeFrom(this.map); - } - } - } - - - setMarkerLabels(marks, visible) { - for (let i = 0; i < marks.length; ++i) { - if (visible) { - marks[i].tooltip.addTo(this.map); - } else { - marks[i].tooltip.removeFrom(this.map); - } - } - } - - -} - - -export default MapHelper; diff --git a/src/js/ui/ZoomSlider.js b/src/js/ui/ZoomSlider.js deleted file mode 100644 index 237d0d4..0000000 --- a/src/js/ui/ZoomSlider.js +++ /dev/null @@ -1,56 +0,0 @@ -class ZoomSlider { - - - constructor(map) { - this._map = map; - this._container = document.querySelector('#zoom-slider'); - this._slider = document.querySelector('#slider-position'); - this._zoomRange = this._map.getMaxZoom() - this._map.getMinZoom(); - this._timeoutId = -1; - this._events(); - } - - - _events() { - this._map.on('zoomstart', () => { - clearTimeout(this._timeoutId); - this._timeoutId = -1; - this._container.classList.add('opened'); - }); - - this._map.on('zoomend', () => { - const correctedZoom = this._map.getZoom() - this._map.getMinZoom(); - this._slider.style.height = `${(correctedZoom * 100) / this._zoomRange}%`; - this._timeoutId = setTimeout(() => this._container.classList.remove('opened'), 1500); - }); - - this._map.on('zoom', () => { - clearTimeout(this._timeoutId); - this._timeoutId = -1; - const correctedZoom = this._map.getZoom() - this._map.getMinZoom(); - this._slider.style.height = `${(correctedZoom * 100) / this._zoomRange}%`; - }); - - this._container.addEventListener('mouseover', () => { - clearTimeout(this._timeoutId); - this._timeoutId = -1; - }); - - this._container.addEventListener('mouseleave', () => { - this._timeoutId = setTimeout(() => this._container.classList.remove('opened'), 1500); - }); - - this._container.querySelector('#zoom-more').addEventListener('click', () => { - this._map.setZoom(this._map.getZoom() + 1); - }); - - this._container.querySelector('#zoom-less').addEventListener('click', () => { - this._map.setZoom(this._map.getZoom() - 1); - }); - } - - -} - - -export default ZoomSlider; diff --git a/src/js/utils/LangManager.js b/src/js/utils/LangManager.js deleted file mode 100644 index 5423399..0000000 --- a/src/js/utils/LangManager.js +++ /dev/null @@ -1,82 +0,0 @@ -import Utils from './Utils.js'; - - -class LangManager { - - - constructor(lang, cb) { - this._lang = (Utils.SUPPORTED_LANGUAGE.indexOf(lang) !== -1) ? lang : 'en'; - this._values = {}; - this._init().then(cb); - } - - - _init() { - return new Promise((resolve, reject) => { - Utils.fetchFile(`assets/nls/${this._lang}.json`).then(lang => { - this._values = JSON.parse(lang); - resolve(); - }).catch(reject); - }); - } - - - debug(key) { - return this._values.debug[key] || ''; - } - - - notif(key) { - return this._values.notif[key] || ''; - } - - - nav(key) { - return this._values.nav[key] || ''; - } - - - map(key) { - return this._values.map[key] || ''; - } - - - spot(key) { - return this._values.spot[key] || ''; - } - - - store(key) { - return this._values.store[key] || ''; - } - - - bar(key) { - return this._values.bar[key] || ''; - } - - - popup(key) { - return this._values.popup[key] || ''; - } - - - modal(key) { - return this._values.modal[key] || ''; - } - - - login(key) { - return this._values.auth.login[key] || ''; - } - - - register(key) { - return this._values.auth.register[key] || ''; - } - - -} - - -export default LangManager; diff --git a/src/js/utils/MarkerEnum.js b/src/js/utils/MarkerEnum.js deleted file mode 100644 index 882f113..0000000 --- a/src/js/utils/MarkerEnum.js +++ /dev/null @@ -1,82 +0,0 @@ -export default Object.freeze({ - blue: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-blue.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - gold: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-gold.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - red: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-red.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - green: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-green.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - orange: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-orange.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - yellow: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-yellow.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - violet: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-violet.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - grey: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-grey.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - black: new window.L.Icon({ - iconUrl: 'assets/img/marker/marker-icon-black.png', - shadowUrl: 'assets/img/marker/marker-shadow.png', - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], - }), - user: new window.L.Icon({ - iconUrl: 'assets/img/marker/user-position.png', - shadowUrl: 'assets/img/marker/user-position-shadow.png', - iconSize: [32, 32], - iconAnchor: [16, 16], - popupAnchor: [1, -34], - shadowSize: [32, 32], - }), -}); diff --git a/src/js/utils/ProviderEnum.js b/src/js/utils/ProviderEnum.js deleted file mode 100644 index 6cb43e8..0000000 --- a/src/js/utils/ProviderEnum.js +++ /dev/null @@ -1,32 +0,0 @@ -export default Object.freeze({ - planOsm: window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap', - maxZoom: 21, - maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19 - minZoom: 2 // Don't allow dezooming too far from map so it always stay fully visible - }), - /*planGeo: window.L.tileLayer('https://wxs.ign.fr/{apikey}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER=GEOGRAPHICALGRIDSYSTEMS.PLANIGNV2&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}', { - attribution: '© Geoportail France', - apikey: 'choisirgeoportail', - format: 'image/png', - style: 'normal', - minZoom: 2, // Don't allow dezooming too far from map so it always stay fully visible - maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19 - maxZoom: 21, - }),*/ - satEsri: window.L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { - attribution: '© Esri Imagery', - minZoom: 2, // Don't allow dezooming too far from map so it always stay fully visible - maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19 - maxZoom: 21 - }), - /*satGeo: window.L.tileLayer('https://wxs.ign.fr/{apikey}/geoportail/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&STYLE={style}&TILEMATRIXSET=PM&FORMAT={format}&LAYER=ORTHOIMAGERY.ORTHOPHOTOS&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}', { - attribution: '© Geoportail France', - apikey: 'choisirgeoportail', - format: 'image/jpeg', - style: 'normal', - minZoom: 2, // Don't allow dezooming too far from map so it always stay fully visible - maxNativeZoom: 19, // To ensure tiles are not unloaded when zooming after 19 - maxZoom: 21 - })*/ -}); diff --git a/src/js/utils/Utils.js b/src/js/utils/Utils.js deleted file mode 100644 index 1a6b150..0000000 --- a/src/js/utils/Utils.js +++ /dev/null @@ -1,269 +0,0 @@ -class Utils { - - - constructor() { /* Not meant to be instantiated, all methods should be static */ } - - - static fetchTemplate(url) { - return new Promise((resolve, reject) => { - fetch(url).then(data => { - data.text().then(html => { - resolve(document.createRange().createContextualFragment(html)); - }).catch(reject); - }).catch(reject); - }); - } - - - static fetchFile(url) { - return new Promise((resolve, reject) => { - fetch(url).then(data => { - data.text().then(resolve).catch(reject); - }).catch(reject); - }); - } - - - static getReq(url) { - return new Promise((resolve, reject) => { - const options = { - method: 'GET', - headers: new Headers(), - mode: 'cors', - cache: 'default' - }; - - fetch(url, options).then(data => { - data.json().then(resolve).catch(reject); - }).catch(reject); - }); - } - - - static postReq(url, data) { - return new Promise((resolve, reject) => { - const options = { - method: 'POST', - headers: new Headers(), - mode: 'cors', - cache: 'default', - body: data - }; - - fetch(url, options).then(data => { - data.json().then(resolve).catch(reject); - }).catch(reject); - }); - } - - - static stripDom(html){ - let doc = new DOMParser().parseFromString(html, 'text/html'); - return doc.body.textContent || ''; - } - - - static replaceString(element, string, value) { - element.innerHTML = element.innerHTML.replace(string, value); - } - - - static getDistanceBetweenCoords(from, to) { - // return distance in meters - var lon1 = (from[1] * Math.PI) / 180, - lat1 = (from[0] * Math.PI) / 180, - lon2 = (to[1] * Math.PI) / 180, - lat2 = (to[0] * Math.PI) / 180; - - var deltaLat = lat2 - lat1; - var deltaLon = lon2 - lon1; - - var a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2); - var c = 2 * Math.asin(Math.sqrt(a)); - var EARTH_RADIUS = 6371; - return c * EARTH_RADIUS * 1000; - } - - - /** @method - * @name precisionRound - * @public - * @memberof Utils - * @author Arthur Beaulieu - * @since September 2018 - * @description Do a Math.round with a given precision (ie amount of integers after the coma) - * @param {nunmber} value - The value to precisely round - * @param {number} precision - The number of integers after the coma - * @return {number} - The rounded value */ - static precisionRound(value, precision) { - const multiplier = Math.pow(10, precision || 0); - return Math.round(value * multiplier) / multiplier; - } - - - static initDebugInterface() { - const lang = window.BeerCrackerz.nls.debug.bind(window.BeerCrackerz.nls); - const debugContainer = document.createElement('DIV'); - const userLat = document.createElement('P'); - const userLng = document.createElement('P'); - const updatesAmount = document.createElement('P'); - const userAccuracy = document.createElement('P'); - const highAccuracy = document.createElement('P'); - const maxAge = document.createElement('P'); - const posTimeout = document.createElement('P'); - const zoomLevel = document.createElement('P'); - const marks = document.createElement('P'); - const exportData = document.createElement('BUTTON'); - debugContainer.classList.add('debug-container'); - userLat.classList.add('debug-user-lat'); - userLng.classList.add('debug-user-lng'); - updatesAmount.classList.add('debug-updates-amount'); - userAccuracy.classList.add('debug-user-accuracy'); - highAccuracy.classList.add('debug-high-accuracy'); - maxAge.classList.add('debug-pos-max-age'); - posTimeout.classList.add('debug-pos-timeout'); - zoomLevel.classList.add('debug-zoom-level'); - marks.classList.add('debug-marks-amount'); - exportData.classList.add('debug-export-data'); - userLat.innerHTML = `${lang('lat')} : -`; - userLng.innerHTML = `${lang('lng')} : -`; - updatesAmount.innerHTML = `${lang('updates')} : 0`; - userAccuracy.innerHTML = `${lang('accuracy')} : -`; - highAccuracy.innerHTML = `${lang('highAccuracy')} : -`; - maxAge.innerHTML = `${lang('posAge')} : -`; - posTimeout.innerHTML = `${lang('posTimeout')} : -`; - zoomLevel.innerHTML = `${lang('zoom')} : -`; - marks.innerHTML = `${lang('marks')} : -`; - exportData.innerHTML = lang('export'); - debugContainer.appendChild(userLat); - debugContainer.appendChild(userLng); - debugContainer.appendChild(updatesAmount); - debugContainer.appendChild(userAccuracy); - debugContainer.appendChild(highAccuracy); - debugContainer.appendChild(maxAge); - debugContainer.appendChild(posTimeout); - debugContainer.appendChild(zoomLevel); - debugContainer.appendChild(marks); - debugContainer.appendChild(exportData); - exportData.addEventListener('click', window.BeerCrackerz.downloadData.bind(window.BeerCrackerz)); - return debugContainer; - } - - - static updateDebugInterface(element, user, options) { - if (window.DEBUG === true) { - const bc = window.BeerCrackerz; - const lang = bc.nls.debug.bind(bc.nls); - const updates = parseInt(element.querySelector('.debug-updates-amount').innerHTML.split(' : ')[1]) + 1; - const marks = bc.marks.spot.length + bc.marks.store.length + bc.marks.bar.length; - element.querySelector('.debug-user-lat').innerHTML = ` - ${lang('lat')} : ${user.lat} - `; - element.querySelector('.debug-user-lng').innerHTML = ` - ${lang('lng')} : ${user.lng} - `; - element.querySelector('.debug-updates-amount').innerHTML = ` - ${lang('updates')} : ${updates} - `; - element.querySelector('.debug-user-accuracy').innerHTML = ` - ${lang('accuracy')} : ${Utils.precisionRound(user.accuracy, 2)}m - `; - element.querySelector('.debug-high-accuracy').innerHTML = ` - ${lang('highAccuracy')} : ${options.enableHighAccuracy === true ? lang('enabled') : lang('disabled')} - `; - element.querySelector('.debug-pos-max-age').innerHTML = ` - ${lang('posAge')} : ${options.maximumAge / 1000}s - `; - element.querySelector('.debug-pos-timeout').innerHTML = ` - ${lang('posTimeout')} : ${options.timeout / 1000}s - `; - element.querySelector('.debug-zoom-level').innerHTML = ` - ${lang('zoom')} : ${bc.map.getZoom()} - `; - element.querySelector('.debug-marks-amount').innerHTML = ` - ${lang('marks')} : ${marks} - `; - } - } - - - static getPreference(pref) { - return localStorage.getItem(pref) || null; - } - - - static setPreference(pref, value) { - localStorage.setItem(pref, value); - } - - - static get RANGE_COLOR() { - return '#ffd87d'; - } - - - static get USER_COLOR() { - return '#63fff5'; - } - - - static get SPOT_COLOR() { - return '#26ad23'; - } - - - static get STORE_COLOR() { - return '#247dc9'; - } - - - static get BAR_COLOR() { - return '#ca2a3d'; - } - - - static get CIRCLE_RADIUS() { - return 100; - } - - - static get NEW_MARKER_RANGE() { - return 200; - } - - - static get MAP_BOUNDS() { - return window.L.latLngBounds( - window.L.latLng(-89.98155760646617, -180), - window.L.latLng(89.99346179538875, 180) - ); - } - - - static get HIGH_ACCURACY() { - return { - enableHighAccuracy: true, // More consuption, better position - maximumAge: 1000, // A position will last 1s maximum - timeout: 900, // A position is updated in 0.9s maximum - }; - } - - - static get OPTIMIZED_ACCURACY() { - return { - enableHighAccuracy: false, // Less consuption - maximumAge: 30000, // A position will last 30s maximum - timeout: 29000 // A position is updated in 29s maximum - }; - } - - - static get SUPPORTED_LANGUAGE() { - return ['en', 'fr', 'es', 'de']; - } - - -} - - -export default Utils; diff --git a/src/scss/_base.scss b/src/scss/_base.scss deleted file mode 100644 index 478e564..0000000 --- a/src/scss/_base.scss +++ /dev/null @@ -1,88 +0,0 @@ -* { - box-sizing: border-box; - margin: 0; - padding: 0; -} - -html, body { - font-size: 62.5%; - height: 100%; - overflow: hidden; - width: 100%; -} - -body { - background: #181818; - color: #D4D4D4; - font-family: sans-serif; - position: relative; -} - -h1 { - color: #181818; - font-size: 2.8rem; - margin-bottom: 1.2rem; -} - -h2 { - font-size: 2.4rem; -} - -a { - color: #a1ff86; - font-size: 1.2rem; - margin-bottom: 1.2rem; -} - -p, label { - color: #2e2e2e; - font-size: 1.2rem; - margin-bottom: 1.2rem; -} - -label { - font-style: italic; -} - -input, textarea { - border: solid 1px #424242; - border-radius: .5rem; - display: block; - margin: .5rem auto; - margin-bottom: 1.2rem; - padding: .5rem; - width: 100%; - - transition: border .2s; -} - -input.error { - border-color: #ff5454; -} - -button { - background-color: #e9e9e9aa; - border: solid 1px #424242; - border-radius: .5rem; - cursor: pointer; - display: block; - margin: .5rem auto; - padding: .5rem; - width: 100%; - - transition: background-color .2s; - - &:active, - &:focus, - &:hover { - background-color: #d9d9d9aa; - } - - &.validate { - background-color: #a1ff86aa; - } - - &.cancel { - background-color: #ff9a9aaa; - } -} diff --git a/src/scss/_keyframes.scss b/src/scss/_keyframes.scss deleted file mode 100644 index 4265523..0000000 --- a/src/scss/_keyframes.scss +++ /dev/null @@ -1,69 +0,0 @@ -@keyframes flashing-logo { - 0% { - background: linear-gradient(60deg, #97EA9B 0%, #AD7FE6 80%); - background-clip: text; - -webkit-text-fill-color: transparent; - opacity: 0; - } - - 15% { - background: linear-gradient(120deg, #97EA9B 0%, #AD7FE6 80%); - background-clip: text; - -webkit-text-fill-color: transparent; - opacity: 0; - } - - 58% { - background: linear-gradient(180deg, #97EA9B 0%, #AD7FE6 80%); - background-clip: text; - -webkit-text-fill-color: transparent; - opacity: 1; - transform: scale(1.1); - } - - 100% { - background: linear-gradient(240deg, #97EA9B 0%, #AD7FE6 80%); - background-clip: text; - -webkit-text-fill-color: transparent; - opacity: 0; - transform: scale(1); - } -} - -@keyframes drop-nav-link { - 0% { - margin-bottom: 20rem; - transform: rotate(-12deg); - } - - 100% { - margin-bottom: 0; - transform: rotate(0); - } -} - -@keyframes beating { - 0% { - transform: scale(1); - } - - 10% { - transform: scale(1.01); - } - - 20% { - transform: scale(1); - } - - 80% { - transform: scale(1); - } - - 90% { - transform: scale(1.02); - } - - 100% { - transform: scale(1); - } -} diff --git a/src/scss/_responsive.scss b/src/scss/_responsive.scss deleted file mode 100644 index 18e4066..0000000 --- a/src/scss/_responsive.scss +++ /dev/null @@ -1,17 +0,0 @@ -@media (min-width: 720px) { - .overlay [class$="-modal"] { - max-width: 50%; - } -} -/* -@media (max-width: 512px) { - -} - -@media (max-height: 740px) { - -} - -@media (max-height: 512px) { - -}*/ diff --git a/static/img/about/Arthur Beaulieu.jpg b/static/img/about/Arthur Beaulieu.jpg new file mode 100644 index 0000000..22f6286 Binary files /dev/null and b/static/img/about/Arthur Beaulieu.jpg differ diff --git a/static/img/about/ArthurBeaulieu.jpg b/static/img/about/ArthurBeaulieu.jpg new file mode 100644 index 0000000..22f6286 Binary files /dev/null and b/static/img/about/ArthurBeaulieu.jpg differ diff --git a/static/img/about/Asiberus.jpg b/static/img/about/Asiberus.jpg new file mode 100644 index 0000000..20c8925 Binary files /dev/null and b/static/img/about/Asiberus.jpg differ diff --git a/static/img/about/Raphael Beekmann.jpg b/static/img/about/Raphael Beekmann.jpg new file mode 100644 index 0000000..20c8925 Binary files /dev/null and b/static/img/about/Raphael Beekmann.jpg differ diff --git a/static/img/doc/bar-mark.webp b/static/img/doc/bar-mark.webp new file mode 100644 index 0000000..7825e8e Binary files /dev/null and b/static/img/doc/bar-mark.webp differ diff --git a/static/img/doc/commands.webp b/static/img/doc/commands.webp new file mode 100644 index 0000000..14db25c Binary files /dev/null and b/static/img/doc/commands.webp differ diff --git a/static/img/doc/shop-mark.webp b/static/img/doc/shop-mark.webp new file mode 100644 index 0000000..a0f361c Binary files /dev/null and b/static/img/doc/shop-mark.webp differ diff --git a/static/img/doc/spot-mark.webp b/static/img/doc/spot-mark.webp new file mode 100644 index 0000000..4280b96 Binary files /dev/null and b/static/img/doc/spot-mark.webp differ diff --git a/static/img/doc/user-position.webp b/static/img/doc/user-position.webp new file mode 100644 index 0000000..98f9781 Binary files /dev/null and b/static/img/doc/user-position.webp differ diff --git a/static/img/favicon/android-chrome-512x512.png b/static/img/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..f4f03f0 Binary files /dev/null and b/static/img/favicon/android-chrome-512x512.png differ diff --git a/static/img/favicon/android-icon-144x144.png b/static/img/favicon/android-icon-144x144.png new file mode 100644 index 0000000..5df83ec Binary files /dev/null and b/static/img/favicon/android-icon-144x144.png differ diff --git a/static/img/favicon/android-icon-192x192.png b/static/img/favicon/android-icon-192x192.png new file mode 100644 index 0000000..8c06139 Binary files /dev/null and b/static/img/favicon/android-icon-192x192.png differ diff --git a/static/img/favicon/android-icon-36x36.png b/static/img/favicon/android-icon-36x36.png new file mode 100644 index 0000000..d080461 Binary files /dev/null and b/static/img/favicon/android-icon-36x36.png differ diff --git a/static/img/favicon/android-icon-48x48.png b/static/img/favicon/android-icon-48x48.png new file mode 100644 index 0000000..cb5aa75 Binary files /dev/null and b/static/img/favicon/android-icon-48x48.png differ diff --git a/static/img/favicon/android-icon-72x72.png b/static/img/favicon/android-icon-72x72.png new file mode 100644 index 0000000..dff1547 Binary files /dev/null and b/static/img/favicon/android-icon-72x72.png differ diff --git a/static/img/favicon/android-icon-96x96.png b/static/img/favicon/android-icon-96x96.png new file mode 100644 index 0000000..34ef26b Binary files /dev/null and b/static/img/favicon/android-icon-96x96.png differ diff --git a/static/img/favicon/apple-icon-114x114.png b/static/img/favicon/apple-icon-114x114.png new file mode 100644 index 0000000..8cf07f2 Binary files /dev/null and b/static/img/favicon/apple-icon-114x114.png differ diff --git a/static/img/favicon/apple-icon-120x120.png b/static/img/favicon/apple-icon-120x120.png new file mode 100644 index 0000000..bf8098b Binary files /dev/null and b/static/img/favicon/apple-icon-120x120.png differ diff --git a/static/img/favicon/apple-icon-144x144.png b/static/img/favicon/apple-icon-144x144.png new file mode 100644 index 0000000..5df83ec Binary files /dev/null and b/static/img/favicon/apple-icon-144x144.png differ diff --git a/static/img/favicon/apple-icon-152x152.png b/static/img/favicon/apple-icon-152x152.png new file mode 100644 index 0000000..6ff16fc Binary files /dev/null and b/static/img/favicon/apple-icon-152x152.png differ diff --git a/static/img/favicon/apple-icon-180x180.png b/static/img/favicon/apple-icon-180x180.png new file mode 100644 index 0000000..036c720 Binary files /dev/null and b/static/img/favicon/apple-icon-180x180.png differ diff --git a/static/img/favicon/apple-icon-57x57.png b/static/img/favicon/apple-icon-57x57.png new file mode 100644 index 0000000..0d8d41e Binary files /dev/null and b/static/img/favicon/apple-icon-57x57.png differ diff --git a/static/img/favicon/apple-icon-60x60.png b/static/img/favicon/apple-icon-60x60.png new file mode 100644 index 0000000..e517533 Binary files /dev/null and b/static/img/favicon/apple-icon-60x60.png differ diff --git a/static/img/favicon/apple-icon-72x72.png b/static/img/favicon/apple-icon-72x72.png new file mode 100644 index 0000000..dff1547 Binary files /dev/null and b/static/img/favicon/apple-icon-72x72.png differ diff --git a/static/img/favicon/apple-icon-76x76.png b/static/img/favicon/apple-icon-76x76.png new file mode 100644 index 0000000..da863d4 Binary files /dev/null and b/static/img/favicon/apple-icon-76x76.png differ diff --git a/static/img/favicon/apple-icon-precomposed.png b/static/img/favicon/apple-icon-precomposed.png new file mode 100644 index 0000000..2956e78 Binary files /dev/null and b/static/img/favicon/apple-icon-precomposed.png differ diff --git a/static/img/favicon/apple-icon.png b/static/img/favicon/apple-icon.png new file mode 100644 index 0000000..2956e78 Binary files /dev/null and b/static/img/favicon/apple-icon.png differ diff --git a/static/img/favicon/apple-touch-icon.png b/static/img/favicon/apple-touch-icon.png new file mode 100644 index 0000000..8bec76c Binary files /dev/null and b/static/img/favicon/apple-touch-icon.png differ diff --git a/assets/img/favicon/browserconfig.xml b/static/img/favicon/browserconfig.xml similarity index 100% rename from assets/img/favicon/browserconfig.xml rename to static/img/favicon/browserconfig.xml diff --git a/static/img/favicon/favicon-16x16.png b/static/img/favicon/favicon-16x16.png new file mode 100644 index 0000000..c93529f Binary files /dev/null and b/static/img/favicon/favicon-16x16.png differ diff --git a/static/img/favicon/favicon-32x32.png b/static/img/favicon/favicon-32x32.png new file mode 100644 index 0000000..2e615d4 Binary files /dev/null and b/static/img/favicon/favicon-32x32.png differ diff --git a/static/img/favicon/favicon-96x96.png b/static/img/favicon/favicon-96x96.png new file mode 100644 index 0000000..34ef26b Binary files /dev/null and b/static/img/favicon/favicon-96x96.png differ diff --git a/static/img/favicon/favicon.ico b/static/img/favicon/favicon.ico new file mode 100644 index 0000000..1d4ca89 Binary files /dev/null and b/static/img/favicon/favicon.ico differ diff --git a/assets/img/favicon/manifest.json b/static/img/favicon/manifest.json similarity index 100% rename from assets/img/favicon/manifest.json rename to static/img/favicon/manifest.json diff --git a/static/img/favicon/ms-icon-144x144.png b/static/img/favicon/ms-icon-144x144.png new file mode 100644 index 0000000..5df83ec Binary files /dev/null and b/static/img/favicon/ms-icon-144x144.png differ diff --git a/static/img/favicon/ms-icon-150x150.png b/static/img/favicon/ms-icon-150x150.png new file mode 100644 index 0000000..f58edcc Binary files /dev/null and b/static/img/favicon/ms-icon-150x150.png differ diff --git a/static/img/favicon/ms-icon-310x310.png b/static/img/favicon/ms-icon-310x310.png new file mode 100644 index 0000000..49d0f0a Binary files /dev/null and b/static/img/favicon/ms-icon-310x310.png differ diff --git a/static/img/favicon/ms-icon-70x70.png b/static/img/favicon/ms-icon-70x70.png new file mode 100644 index 0000000..dc5feed Binary files /dev/null and b/static/img/favicon/ms-icon-70x70.png differ diff --git a/static/img/favicon/mstile-150x150.png b/static/img/favicon/mstile-150x150.png new file mode 100644 index 0000000..805d229 Binary files /dev/null and b/static/img/favicon/mstile-150x150.png differ diff --git a/static/img/favicon/safari-pinned-tab.svg b/static/img/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000..7a3d09d --- /dev/null +++ b/static/img/favicon/safari-pinned-tab.svg @@ -0,0 +1,44 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + + diff --git a/static/img/favicon/site.webmanifest b/static/img/favicon/site.webmanifest new file mode 100644 index 0000000..b20abb7 --- /dev/null +++ b/static/img/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/assets/img/logo-small.png b/static/img/logo-small.png similarity index 100% rename from assets/img/logo-small.png rename to static/img/logo-small.png diff --git a/static/img/logo-social.png b/static/img/logo-social.png new file mode 100644 index 0000000..b2e7a38 Binary files /dev/null and b/static/img/logo-social.png differ diff --git a/assets/img/logo-text.png b/static/img/logo-text.png similarity index 100% rename from assets/img/logo-text.png rename to static/img/logo-text.png diff --git a/static/img/logo-tiny.png b/static/img/logo-tiny.png new file mode 100644 index 0000000..93dfb5d Binary files /dev/null and b/static/img/logo-tiny.png differ diff --git a/assets/img/logo.png b/static/img/logo.png similarity index 100% rename from assets/img/logo.png rename to static/img/logo.png diff --git a/assets/img/logo.svg b/static/img/logo.svg similarity index 100% rename from assets/img/logo.svg rename to static/img/logo.svg diff --git a/static/img/logo/beach.svg b/static/img/logo/beach.svg new file mode 100644 index 0000000..95f3c6d --- /dev/null +++ b/static/img/logo/beach.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/bench.svg b/static/img/logo/bench.svg new file mode 100644 index 0000000..61b1045 --- /dev/null +++ b/static/img/logo/bench.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/bio.svg b/static/img/logo/bio.svg new file mode 100644 index 0000000..ea49462 --- /dev/null +++ b/static/img/logo/bio.svg @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/can.svg b/static/img/logo/can.svg new file mode 100644 index 0000000..c9a3025 --- /dev/null +++ b/static/img/logo/can.svg @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/card.svg b/static/img/logo/card.svg new file mode 100644 index 0000000..0597344 --- /dev/null +++ b/static/img/logo/card.svg @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/cellar.svg b/static/img/logo/cellar.svg new file mode 100644 index 0000000..6bf7a6e --- /dev/null +++ b/static/img/logo/cellar.svg @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/center.svg b/static/img/logo/center.svg new file mode 100644 index 0000000..4f49dca --- /dev/null +++ b/static/img/logo/center.svg @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/choice.svg b/static/img/logo/choice.svg new file mode 100644 index 0000000..a63ab0f --- /dev/null +++ b/static/img/logo/choice.svg @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/circle.svg b/static/img/logo/circle.svg new file mode 100644 index 0000000..639beaa --- /dev/null +++ b/static/img/logo/circle.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/city.svg b/static/img/logo/city.svg new file mode 100644 index 0000000..4f4b6fe --- /dev/null +++ b/static/img/logo/city.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/cliff.svg b/static/img/logo/cliff.svg new file mode 100644 index 0000000..c1a1891 --- /dev/null +++ b/static/img/logo/cliff.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/comment.svg b/static/img/logo/comment.svg new file mode 100644 index 0000000..e061f65 --- /dev/null +++ b/static/img/logo/comment.svg @@ -0,0 +1,40 @@ + + + + + + diff --git a/static/img/logo/covered.svg b/static/img/logo/covered.svg new file mode 100644 index 0000000..24ca819 --- /dev/null +++ b/static/img/logo/covered.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/craft.svg b/static/img/logo/craft.svg new file mode 100644 index 0000000..496f106 --- /dev/null +++ b/static/img/logo/craft.svg @@ -0,0 +1,834 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/debug.svg b/static/img/logo/debug.svg new file mode 100644 index 0000000..96e3730 --- /dev/null +++ b/static/img/logo/debug.svg @@ -0,0 +1,40 @@ + + + + + + diff --git a/static/img/logo/dollar.svg b/static/img/logo/dollar.svg new file mode 100644 index 0000000..e50b78f --- /dev/null +++ b/static/img/logo/dollar.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/edit.svg b/static/img/logo/edit.svg new file mode 100644 index 0000000..9366f53 --- /dev/null +++ b/static/img/logo/edit.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/filter.svg b/static/img/logo/filter.svg new file mode 100644 index 0000000..4302bc6 --- /dev/null +++ b/static/img/logo/filter.svg @@ -0,0 +1,43 @@ + + + + + + + + diff --git a/static/img/logo/food.svg b/static/img/logo/food.svg new file mode 100644 index 0000000..fc5e81f --- /dev/null +++ b/static/img/logo/food.svg @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/forest.svg b/static/img/logo/forest.svg new file mode 100644 index 0000000..adaf145 --- /dev/null +++ b/static/img/logo/forest.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/static/img/logo/fresh.svg b/static/img/logo/fresh.svg new file mode 100644 index 0000000..1092e4e --- /dev/null +++ b/static/img/logo/fresh.svg @@ -0,0 +1,44 @@ + + + + + + diff --git a/static/img/logo/hyper.svg b/static/img/logo/hyper.svg new file mode 100644 index 0000000..4b66cea --- /dev/null +++ b/static/img/logo/hyper.svg @@ -0,0 +1,38 @@ + + + + + + diff --git a/static/img/logo/info.svg b/static/img/logo/info.svg new file mode 100644 index 0000000..f9d249f --- /dev/null +++ b/static/img/logo/info.svg @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/img/logo/label.svg b/static/img/logo/label.svg similarity index 100% rename from assets/img/logo/label.svg rename to static/img/logo/label.svg diff --git a/static/img/logo/lake.svg b/static/img/logo/lake.svg new file mode 100644 index 0000000..f2419ea --- /dev/null +++ b/static/img/logo/lake.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + diff --git a/static/img/logo/left.svg b/static/img/logo/left.svg new file mode 100644 index 0000000..43c1774 --- /dev/null +++ b/static/img/logo/left.svg @@ -0,0 +1,50 @@ + + + + + + + diff --git a/static/img/logo/mountain.svg b/static/img/logo/mountain.svg new file mode 100644 index 0000000..bcabf79 --- /dev/null +++ b/static/img/logo/mountain.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/night.svg b/static/img/logo/night.svg new file mode 100644 index 0000000..8c9701a --- /dev/null +++ b/static/img/logo/night.svg @@ -0,0 +1,58 @@ + + + + + + ic_fluent_dark_theme_24_regular + Created with Sketch. + + + + + + diff --git a/static/img/logo/outdoor.svg b/static/img/logo/outdoor.svg new file mode 100644 index 0000000..33765a3 --- /dev/null +++ b/static/img/logo/outdoor.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/parking.svg b/static/img/logo/parking.svg new file mode 100644 index 0000000..34b07f0 --- /dev/null +++ b/static/img/logo/parking.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/photo.svg b/static/img/logo/photo.svg new file mode 100644 index 0000000..1badd32 --- /dev/null +++ b/static/img/logo/photo.svg @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/pov.svg b/static/img/logo/pov.svg new file mode 100644 index 0000000..e574fdd --- /dev/null +++ b/static/img/logo/pov.svg @@ -0,0 +1,61 @@ + + + + + +Created by potrace 1.15, written by Peter Selinger 2001-2017 + + + + + + + + diff --git a/static/img/logo/precision.svg b/static/img/logo/precision.svg new file mode 100644 index 0000000..f017df3 --- /dev/null +++ b/static/img/logo/precision.svg @@ -0,0 +1,62 @@ + + + + + + + meter + + + + + + diff --git a/static/img/logo/profile.svg b/static/img/logo/profile.svg new file mode 100644 index 0000000..aefab7d --- /dev/null +++ b/static/img/logo/profile.svg @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/regular.svg b/static/img/logo/regular.svg new file mode 100644 index 0000000..a73086a --- /dev/null +++ b/static/img/logo/regular.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + diff --git a/static/img/logo/right.svg b/static/img/logo/right.svg new file mode 100644 index 0000000..4cd84d6 --- /dev/null +++ b/static/img/logo/right.svg @@ -0,0 +1,50 @@ + + + + + + + diff --git a/static/img/logo/river.svg b/static/img/logo/river.svg new file mode 100644 index 0000000..d866c96 --- /dev/null +++ b/static/img/logo/river.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/rooftop.svg b/static/img/logo/rooftop.svg new file mode 100644 index 0000000..760afb6 --- /dev/null +++ b/static/img/logo/rooftop.svg @@ -0,0 +1,63 @@ + + + + 1050 + + + + + + + + diff --git a/static/img/logo/sea.svg b/static/img/logo/sea.svg new file mode 100644 index 0000000..c9ab7a1 --- /dev/null +++ b/static/img/logo/sea.svg @@ -0,0 +1,54 @@ + + + + + + + + + diff --git a/static/img/logo/snack.svg b/static/img/logo/snack.svg new file mode 100644 index 0000000..4983d01 --- /dev/null +++ b/static/img/logo/snack.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/star.svg b/static/img/logo/star.svg new file mode 100644 index 0000000..65e62a6 --- /dev/null +++ b/static/img/logo/star.svg @@ -0,0 +1,37 @@ + + diff --git a/static/img/logo/store.svg b/static/img/logo/store.svg new file mode 100644 index 0000000..d9acb0c --- /dev/null +++ b/static/img/logo/store.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/super.svg b/static/img/logo/super.svg new file mode 100644 index 0000000..2f68d1f --- /dev/null +++ b/static/img/logo/super.svg @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/tobacco.svg b/static/img/logo/tobacco.svg new file mode 100644 index 0000000..88183c0 --- /dev/null +++ b/static/img/logo/tobacco.svg @@ -0,0 +1,64 @@ + + diff --git a/static/img/logo/toilet.svg b/static/img/logo/toilet.svg new file mode 100644 index 0000000..2a10478 --- /dev/null +++ b/static/img/logo/toilet.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/logo/trash.svg b/static/img/logo/trash.svg new file mode 100644 index 0000000..697ef77 --- /dev/null +++ b/static/img/logo/trash.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/static/img/marker/marker-icon-black.png b/static/img/marker/marker-icon-black.png new file mode 100644 index 0000000..bd38e86 Binary files /dev/null and b/static/img/marker/marker-icon-black.png differ diff --git a/static/img/marker/marker-icon-blue.png b/static/img/marker/marker-icon-blue.png new file mode 100644 index 0000000..fce89b1 Binary files /dev/null and b/static/img/marker/marker-icon-blue.png differ diff --git a/static/img/marker/marker-icon-green.png b/static/img/marker/marker-icon-green.png new file mode 100644 index 0000000..1af9175 Binary files /dev/null and b/static/img/marker/marker-icon-green.png differ diff --git a/static/img/marker/marker-icon-red.png b/static/img/marker/marker-icon-red.png new file mode 100644 index 0000000..7b328be Binary files /dev/null and b/static/img/marker/marker-icon-red.png differ diff --git a/static/img/marker/marker-shadow.png b/static/img/marker/marker-shadow.png new file mode 100644 index 0000000..bab95f2 Binary files /dev/null and b/static/img/marker/marker-shadow.png differ diff --git a/assets/img/marker/user-position-shadow.png b/static/img/marker/user-position-shadow.png similarity index 100% rename from assets/img/marker/user-position-shadow.png rename to static/img/marker/user-position-shadow.png diff --git a/assets/img/marker/user-position.png b/static/img/marker/user-position.png similarity index 100% rename from assets/img/marker/user-position.png rename to static/img/marker/user-position.png diff --git a/static/nls/de.json b/static/nls/de.json new file mode 100644 index 0000000..b54f164 --- /dev/null +++ b/static/nls/de.json @@ -0,0 +1,244 @@ +{ + "debug": { + "lat": "Breite", + "lng": "Längengrad", + "updates": "Aktualisierung", + "accuracy": "Genauigkeit", + "highAccuracy": "Hohe genauigkeit", + "posAge": "Position max alter", + "posTimeout": "Position timeout", + "zoom": "Zoom", + "enabled": "Ermöglicht", + "disabled": "Behinderte", + "marks": "Markierungen" + }, + "notif": { + "geolocationError": "Ihr browser implementiert die geolokalisierungs-API nicht", + "newMarkerOutside": "Neuer marker außerhalb ihrer reichweite", + "spotAdded": "Neuer spot auf der karte gespeichert", + "spotNotAdded": "Spot konnte nicht hinzugefügt werden", + "shopAdded": "Neuer laden auf der karte gespeichert", + "shopNotAdded": "laden konnte nicht hinzugefügt werden", + "barAdded": "Neue bar auf karte gespeichert", + "barNotAdded": "Bar konnte nicht hinzugefügt werden", + "spotEdited": "Spot bearbeitet! Vielen dank", + "spotNotEdited": "Spot konnte nicht bearbeitet werden", + "shopEdited": "Laden bearbeitet! Vielen dank", + "shopNotEdited": "Laden konnte nicht bearbeitet werden", + "barEdited": "Bar bearbeitet! Vielen dank", + "barNotEdited": "Bar konnte nicht bearbeitet werden", + "spotDeleted": "Spot gelöscht", + "spotNotDeleted": "Spot konnte nicht gelöscht werden", + "shopDeleted": "Laden gelöscht", + "shopNotDeleted": "Laden konnte nicht gelöscht werden", + "barDeleted": "Bar gelöscht", + "barNotDeleted": "Bar konnte nicht gelöscht werden", + "markNameEmpty": "Sie haben keinen namen für die marke angegeben", + "markTypeEmpty": "Sie haben keinen typ für die marke angegeben", + "markRateEmpty": "Sie haben keinen bewertung für die marke angegeben", + "markPriceEmpty": "Sie haben keinen preis für die Marke angegeben", + "lockFocusOn": "Zentrieren und sperren der ansicht auf ihre Position", + "unlockFocusOn": "Positionssperre beendet", + "uploadPPSuccess": "Profilbild aktualisiert", + "uploadPPFailed": "Profilbild konnte nicht hochgeladen werden", + "welcomeBack": "Schön, dich wieder auf BeerCrackerz zu sehen!" + }, + "nav": { + "add": "Hinzufügen", + "edit": "Bearbeiten", + "upload": "Hochladen", + "cancel": "Absagen", + "close": "Schließen", + "delete": "Löschen" + }, + "map": { + "newTitle": "Neue Markierung", + "newSpot": "Spot hinzufügen", + "newShop": "Laden hinzufügen", + "newBar": "Bar hinzufügen", + "planLayerOSM": "Karte OSM", + "satLayerEsri": "Satellit ESRI" + }, + "spot": { + "addTitle": "Neuer spot", + "editTitle": "Spot bearbeiten", + "subtitle": "Ein spot ist ein bemerkenswerter ort, um ein bier zu knacken! Teilen sie es mit der community, sei es für die erstaunliche aussicht oder für was auch immer es spaß macht, ein bier zu trinken!", + "nameLabel": "Benennen sie diese spot", + "descLabel": "Warum beschreibst du es nicht", + "rateLabel": "Geben sie ihm eine notiz", + "typeLabel": "Was ist das für ein spot", + "forestType": "Wald", + "riverType": "Fluss", + "lakeType": "See", + "cliffType": "Cliff", + "mountainType": "Berg", + "beachType": "Strand", + "seaType": "Meer", + "cityType": "Stadt", + "povType": "Standpunkt", + "modifiersLabel": "Hat es irgendwelche besonderheiten", + "benchModifier": "Bank", + "coveredModifier": "Bedeckt", + "toiletModifier": "Toilette", + "storeModifier": "Laden in der Nähe", + "trashModifier": "Müll", + "parkingModifier": "Parken" + }, + "shop": { + "addTitle": "Neuer laden", + "editTitle": "Laden bearbeiten", + "subtitle": "Der unverzichtbare ort, um ihren bniervorrat aufzufüllen. Je mehr informationen du gibst, desto besser hilfst du deinen BeerCrackerz-kollegen!", + "nameLabel": "Laden Name", + "descLabel": "Warum beschreibst du es nicht", + "rateLabel": "Geben sie ihm eine notiz", + "priceLabel": "Wie teuer es ist", + "typeLabel": "Was ist das für ein laden", + "storeType": "Lebensmittelmarkt", + "superType": "Supermarkt", + "hyperType": "Hypermarkt", + "cellarType": "Keller", + "modifiersLabel": "Hat es irgendwelche besonderheiten", + "bioModifier": "Bio", + "craftModifier": "Handgemachtes", + "freshModifier": "Gekühlt", + "cardModifier": "Kreditkarte", + "choiceModifier": "Große auswahl" + }, + "bar": { + "addTitle": "Neue bar", + "editTitle": "Bar bearbeiten", + "subtitle": "Eine bar ist ein heiliger ort, an dem sie schön gekühlte biere vom fass bekommen können!", + "nameLabel": "Name der Bar", + "descLabel": "Warum beschreibst du es nicht", + "rateLabel": "Geben sie ihm eine notiz", + "priceLabel": "Wie teuer es ist", + "typeLabel": "Was ist das für eine Bar", + "regularType": "Regulär", + "snackType": "Snack", + "cellarType": "Brauerei", + "rooftopType": "Dach", + "modifiersLabel": "Hat es irgendwelche besonderheiten", + "tobaccoModifier": "Zigaretten", + "foodModifier": "Essen", + "cardModifier": "Kreditkarte", + "choiceModifier": "Große auswahl", + "outdoorModifier": "Draussen" + }, + "modal": { + "userTitle": "Benutzerkonto", + "userAccuracyPref": "Hohe präzision", + "darkThemePref": "Dunkles thema", + "userDebugPref": "Debug-schnittstelle", + "startupHelp": "Einstieg", + "langPref": "Schnittstellensprache", + "langFr": "🇫🇷 Französisch", + "langEn": "🇬🇧 Englisch", + "langEs": "🇪🇸 Spanisch", + "langDe": "🇩🇪 Deutsch", + "langIt": "🇩🇪 Italienisch", + "langPt": "🇵🇹 Portugiesisch", + "aboutTitle": "Über BeerCrackerz", + "aboutDesc": "Eine brillante idee von David Béché!
        BeerCrackerz ist die gemeinschaft der bierliebhaber, gefüllt mit bierjägern und müsliliebhabern.

        Beercrackerz – v0.1.0 – MBP 2022/2023", + "updatePP": "Aktualisieren sie ihr profilbild", + "updatePPSizeError": "Bitte wählen sie ein bild unter 2.5 Mo aus", + "updatePPDimensionError": "Die minimale bildgröße beträgt 512x512", + "updatePPTitle": "Neues profilbild", + "updatePPDesc": "Schneiden Sie das bild bei bedarf zu und legen sie los!", + "logout": "Ausloggen", + "hideShowTitle": "Kartenoptionen", + "hideShowSpots": "Spots", + "hideShowShops": "Läden", + "hideShowBars": "Bars", + "hideShowHelperLabel": "Klicken sie auf ein symbol, um seine beschreibung anzuzeigen", + "spotHelperHideShow": "Zum ausblenden oder anzeigen aller spots auf der karte.", + "shopHelperHideShow": "Zum ausblenden oder anzeigen aller läden auf der karte.", + "barHelperHideShow": "Zum ausblenden oder anzeigen aller bar auf der karte.", + "deleteMarkTitle": "Markierung löschen", + "deleteMarkDesc": "Möchten sie diese markierung wirklich löschen?
        Diese aktion ist dauerhaft und kann nicht rückgängig gemacht werden.", + "spotEditTitle": "Spot bearbeiten", + "shopEditTitle": "Laden bearbeiten", + "barEditTitle": "Bar bearbeiten", + "helpTitle": "Entdecken Sie BeerCrackerz", + "helpNavNext": "Navigieren sie zur nächsten seite oder verlassen sie dieses hilfemenü!", + "helpNavNextPrev": "Navigieren sie zur nächsten oder vorherigen seite oder verlassen sie dieses hilfemenü!", + "helpNavEnd": "Sie können diesen assistenten jetzt schließen!", + "helpPage1": "Willkommen bei BeerCrackerz!
        Mal sehen, wie die Karte funktioniert; Ihre Position wird durch das Symbol \"user-position\" gekennzeichnet. Um ihre position herum zeigt ein blauer kreis die aktuelle Genauigkeit Ihrer Position an. Um sie herum gibt es auch einen goldenen kreis ; Dies ist die maximale entfernung, für die sie einen neuen spot, ein laden oder eine bar deklarieren können.", + "helpPage2": "Ein spot wird durch das symbol \"spot-mark\" dargestellt. Spot sind perfekte orte, um eine bierdose zu genießen. Jeder spot hat seine besonderheiten, es liegt an ihnen, sie zu entdecken.", + "helpPage3": "Ein laden wird durch das symbol \"shop-mark\" dargestellt. Laden sind der ideale ort, um sich mit bierdosen einzudecken. Jeder laden hat seine besonderheiten, es liegt an ihnen, sie zu entdecken.", + "helpPage4": "Ein bar wird durch das symbol \"bar-mark\" dargestellt. Bars sind freundliche orte, die frische und gute biere servieren. Jeder bar hat seine besonderheiten, es liegt an ihnen, sie zu entdecken.", + "helpPage5": "Es stehen mehrere befehle zur verfügung: 1. auswahl der basiskarte, 2. zentrieren und sperren der Karte auf ihrer position, 3. ein oder ausblenden von kartenelementen und 4. ihr konto.", + "helpQuit": "Aufhören", + "helpQuitNoSee": "Aufhören und nie wieder sehen" + }, + "popup": { + "spotFoundBy": "Eine spot, die von entdeckt wurde", + "spotFoundWhen": "Seit der", + "shopFoundBy": "Ein laden hinzugefügt von", + "shopFoundWhen": "Seit der", + "barFoundBy": "Ein bar hinzugefügt von", + "barFoundWhen": "Seit der", + "spotNoDesc": "Für diesen spot ist keine beschreibung verfügbar", + "shopNoDesc": "Für diesen laden ist keine beschreibung verfügbar", + "barNoDesc": "Für diesen bar ist keine beschreibung verfügbar" + }, + "auth": { + "login": { + "headTitle": "Verbindung | BeerCrackerz", + "subtitle": "Siehe anschluss", + "hiddenError": "Oh! Ein versteckter text!", + "username": "Benutzername", + "password": "Passwort", + "login": "Siehe anschluss", + "notRegistered": "Nicht registriert?", + "register": "Ein konto erstellen", + "forgot": "Passwort vergessen?", + "reset": "Setzen sie es zurück", + "bothEmpty": "Bitte geben sie einen benutzernamen und ein passwort ein", + "usernameEmpty": "Bitte geben sie einen benutzernamen ein", + "passwordEmpty": "Bitte geben sie ihr passwort ein", + "credsInvalid": "Falscher Benutzername oder falsches Passwort", + "serverError": "Ein serverfehler ist aufgetreten, wenden sie sich an den support", + "checkMail": "Eine E-Mail wurde gesendet, damit sie fortfahren können" + }, + "register": { + "headTitle": "Registrieren | BeerCrackerz", + "subtitle": "Registrieren", + "hiddenError": "Oh! Ein versteckter text!", + "username": "Nutzername", + "mail": "E-Mail-Addresse", + "password1": "Passwort", + "password2": "Kennwort bestätigen", + "register": "Registrieren", + "notRegistered": "Bereits registriert?", + "login": "Siehe anschluss", + "fieldEmpty": "Bitte füllen sie alle felder des formulars aus", + "notMatchingPassword": "Die zwei passwörter stimmen nicht überein", + "activationSuccess": "Ihr konto wurde erfolgreich aktiviert!", + "activationError": "Fehler beim aktivieren ihres kontos" + }, + "forgotPassword": { + "headTitle": "Passwort vergessen | BeerCrackerz", + "subtitle": "Passwort vergessen", + "hiddenError": "Oh! Ein versteckter text!", + "mail": "E-Mail-Addresse", + "submit": "Setze dein passwort zurück", + "loginLabel": "Du erinnerst dich?", + "login": "Siehe anschluss", + "fieldEmpty": "Geben sie ihre e-mail-adresse ein", + "serverError": "Ein serverfehler ist aufgetreten, wenden sie sich an den support" + }, + "resetPassword": { + "headTitle": "Passwort zurücksetzen | BeerCrackerz", + "subtitle": "Passwort zurücksetzen", + "hiddenError": "Oh! Ein versteckter text!", + "password1": "Passwort", + "password2": "Kennwort bestätigen", + "reset": "Setze dein passwort zurück", + "loginLabel": "Du erinnerst dich?", + "login": "Siehe anschluss", + "fieldEmpty": "Bitte füllen sie alle felder des formulars aus", + "notMatchingPassword": "Die zwei passwörter stimmen nicht überein", + "serverError": "Ein serverfehler ist aufgetreten, wenden sie sich an den support" + } + } +} diff --git a/static/nls/en.json b/static/nls/en.json new file mode 100644 index 0000000..14f81d3 --- /dev/null +++ b/static/nls/en.json @@ -0,0 +1,244 @@ +{ + "debug": { + "lat": "Latitude", + "lng": "Longitude", + "updates": "Updates", + "accuracy": "Accuracy", + "highAccuracy": "High accuracy", + "posAge": "Position max age", + "posTimeout": "Position timeout", + "zoom": "Zoom level", + "enabled": "Enabled", + "disabled": "Disabled", + "marks": "Marks" + }, + "notif": { + "geolocationError": "Your browser doesn't implement the geolocation API", + "newMarkerOutside": "New marker out of your range", + "spotAdded": "New spot saved to map", + "spotNotAdded": "Couldn't add spot", + "shopAdded": "New shop saved to map", + "shopNotAdded": "Couldn't add shop", + "barAdded": "New bar saved to map", + "barNotAdded": "Couldn't add bar", + "spotEdited": "Spot edited! Thanks", + "spotNotEdited": "Couldn't edit spot", + "shopEdited": "Shop edited! Thanks", + "shopNotEdited": "Couldn't edit shop", + "barEdited": "Bar edited! Thanks", + "barNotEdited": "Couldn't edit bar", + "spotDeleted": "Spot deleted", + "spotNotDeleted": "Couldn't delete spot", + "shopDeleted": "Shop deleted", + "shopNotDeleted": "Couldn't delete shop", + "barDeleted": "Bar deleted", + "barNotDeleted": "Couldn't delete bar", + "markNameEmpty": "You didn't specified a name for the mark", + "markTypeEmpty": "You didn't specified a type for the mark", + "markRateEmpty": "You didn't specified a rate for the mark", + "markPriceEmpty": "You didn't specified a price for the mark", + "lockFocusOn": "Centering and locking view on your position", + "unlockFocusOn": "Position locking ended", + "uploadPPSuccess": "Profile picture updated", + "uploadPPFailed": "Failed to upload profile picture", + "welcomeBack": "Glad to see you back on BeerCrackerz!" + }, + "nav": { + "add": "Add", + "edit": "Edit", + "upload": "Upload", + "cancel": "Cancel", + "close": "Close", + "delete": "Delete" + }, + "map": { + "newTitle": "New marker", + "newSpot": "Add a spot", + "newShop": "Add a shop", + "newBar": "Add a bar", + "planLayerOSM": "Plan OSM", + "satLayerEsri": "Satellite ESRI" + }, + "spot": { + "addTitle": "New spot", + "editTitle": "Edit spot", + "subtitle": "A spot is a remarkable place to crack a beer ! Share it with the community, wether it is for the astonishing view or for whatever it is enjoyable to drink a beer!", + "nameLabel": "Name that spot", + "descLabel": "Why don't you describe it", + "rateLabel": "Give it a note", + "typeLabel": "What kind of spot it is", + "forestType": "Forest", + "riverType": "River", + "lakeType": "Lake", + "cliffType": "Cliff", + "mountainType": "Mountain", + "beachType": "Beach", + "seaType": "Sea", + "cityType": "City", + "povType": "Point of View", + "modifiersLabel": "Has it any specific features", + "benchModifier": "Bench", + "coveredModifier": "Covered", + "toiletModifier": "Toilet", + "storeModifier": "Store nearby", + "trashModifier": "Trash", + "parkingModifier": "Parking" + }, + "shop": { + "addTitle": "New shop", + "editTitle": "Edit shop", + "subtitle": "The must have place to refill your beer stock. The more info you provide, the better you help your fellow beer crackerz!", + "nameLabel": "Shop name", + "descLabel": "Why don't you describe it", + "rateLabel": "Give it a note", + "priceLabel": "How expensive it is", + "typeLabel": "What kind of shop it is", + "storeType": "Grocery store", + "superType": "Supermarket", + "hyperType": "Hypermarket", + "cellarType": "Cellar", + "modifiersLabel": "Has it any specific features", + "bioModifier": "Bio", + "craftModifier": "Craft", + "freshModifier": "Refrigerated", + "cardModifier": "Credit card", + "choiceModifier": "Wide choice" + }, + "bar": { + "addTitle": "New bar", + "editTitle": "Edit bar", + "subtitle": "A bar is a holly place where you can get some nicely colded draft beers!", + "nameLabel": "Bar name", + "descLabel": "Why don't you describe it", + "rateLabel": "Give it a note", + "priceLabel": "How expensive it is", + "typeLabel": "What kind of bar it is", + "regularType": "Regular", + "snackType": "Snack", + "cellarType": "Brewery", + "rooftopType": "Rooftop", + "modifiersLabel": "Has it any specific features", + "tobaccoModifier": "Cigarets", + "foodModifier": "Food", + "cardModifier": "Credit card", + "choiceModifier": "Wide choice", + "outdoorModifier": "Outdoor" + }, + "modal": { + "userTitle": "User account", + "userAccuracyPref": "High precision", + "darkThemePref": "Dark theme", + "userDebugPref": "Debug interface", + "startupHelp": "Starting help", + "langPref": "Interface language", + "langFr": "🇫🇷 French", + "langEn": "🇬🇧 English", + "langEs": "🇪🇸 Spanish", + "langDe": "🇩🇪 German", + "langIt": "🇩🇪 Italian", + "langPt": "🇵🇹 Portuguese", + "aboutTitle": "About BeerCrackerz", + "aboutDesc": "A brilliant idea from David Béché!
        BeerCrackerz is the beer lovers comunity, filled with pint slayers and cereals lovers.

        Beercrackerz – v0.1.0 – MBP 2022/2023", + "updatePP": "Update your profile pic", + "updatePPSizeError": "Please select an image below 2.5 Mo", + "updatePPDimensionError": "The minimum image size is 512x512", + "updatePPTitle": "New profile picture pic", + "updatePPDesc": "Crop the picture if needed and go!", + "logout": "Sign out", + "hideShowTitle": "Map options", + "hideShowSpots": "Spots", + "hideShowShops": "Shops", + "hideShowBars": "Bars", + "hideShowHelperLabel": "Click on an icon to display its description", + "spotHelperHideShow": "To hide or show all spots on the map.", + "shopHelperHideShow": "To hide or show all shops on the map.", + "barHelperHideShow": "To hide or show all bars on the map.", + "deleteMarkTitle": "Delete mark", + "deleteMarkDesc": "Are you sure you want to delete this mark?
        This action is permanent and can not be reverted.", + "spotEditTitle": "Edit spot", + "shopEditTitle": "Edit shop", + "barEditTitle": "Edit bar", + "helpTitle": "Discover BeerCrackerz", + "helpNavNext": "Navigate to the next page, or exit this help menu!", + "helpNavNextPrev": "Navigate to the next or previous page, or exit this help menu!", + "helpNavEnd": "You can now close this wizard!", + "helpPage1": "Welcome to BeerCrackerz!
        Let's see how the map works; your position is marked by the symbol \"user-position\". Around your position, a blue circle represents the current accuracy of your position. Around you, there is also a golden circle ; this is the maximum distance for which you can declare a new spot, a store or a bar.", + "helpPage2": "A spot is represented by the symbol \"spot-mark\". Spots are perfect places to enjoy a beer can. Each spot has its specificities, it's up to you to discover them.", + "helpPage3": "A shop is represented by the symbol \"shop-mark\". Shops are the ideal places to stock up on beer cans. Each shop has its specificities, it's up to you to discover them.", + "helpPage4": "A bar is represented by the symbol \"bar-mark\". Bars are friendly places that serve fresh and good beers. Each bar has its specificities, it's up to you to discover them.", + "helpPage5": "Several commands are available : 1. base map choice, 2. recenter and lock the map on your position, 3. show or hide map items and 4. your account.", + "helpQuit": "Quit", + "helpQuitNoSee": "Quit and don't see again" + }, + "popup": { + "spotFoundBy": "A spot discovered by", + "spotFoundWhen": "Since the", + "shopFoundBy": "A shop added by", + "shopFoundWhen": "Since the", + "barFoundBy": "A bar added by", + "barFoundWhen": "Since the", + "spotNoDesc": "No description available for this spot", + "shopNoDesc": "No description available for this shop", + "barNoDesc": "No description available for this bar" + }, + "auth": { + "login": { + "headTitle": "Login | BeerCrackerz", + "subtitle": "Login", + "hiddenError": "Oh! A hidden text!", + "username": "Username", + "password": "Password", + "login": "Login", + "notRegistered": "Not registered yet?", + "register": "Create an account", + "forgot": "Forgot password?", + "reset": "Reset it", + "bothEmpty": "Please fill the username and password fields", + "usernameEmpty": "Please fill the username field", + "passwordEmpty": "Please fill your password field", + "credsInvalid": "Invalid credentials", + "serverError": "Something wrong happened, contact support", + "checkMail": "An email has been sent so you can proceed" + }, + "register": { + "headTitle": "Register | BeerCrackerz", + "subtitle": "Create an account", + "hiddenError": "Oh! A hidden text!", + "username": "Username", + "mail": "Email address", + "password1": "Password", + "password2": "Confirm password", + "register": "Register", + "notRegistered": "Already registered?", + "login": "Login", + "fieldEmpty": "Please fill all required fields", + "notMatchingPassword": "Passwords doesn't match", + "activationSuccess": "Your account has been successfully created!", + "activationError": "Something wrong happened while creating your acocunt, contact support" + }, + "forgotPassword": { + "headTitle": "Forgot password | BeerCrackerz", + "subtitle": "Forgot password", + "hiddenError": "Oh! A hidden text!", + "mail": "Email", + "submit": "Reset password", + "loginLabel": "You remember it?", + "login": "Login", + "fieldEmpty": "Please fill al required fields", + "serverError": "Something wrong happened, contact support" + }, + "resetPassword": { + "headTitle": "Reset password | BeerCrackerz", + "subtitle": "Reset password", + "hiddenError": "Oh! A hidden text!", + "password1": "New password", + "password2": "Confirm password", + "reset": "Reset password", + "loginLabel": "You remember it?", + "login": "Login", + "fieldEmpty": "Please fill all fields", + "notMatchingPassword": "Passwords doesn't match", + "serverError": "Something wrong happened, contact support" + } + } +} diff --git a/static/nls/es.json b/static/nls/es.json new file mode 100644 index 0000000..d82cf50 --- /dev/null +++ b/static/nls/es.json @@ -0,0 +1,244 @@ +{ + "debug": { + "lat": "Latitud", + "lng": "Longitud", + "updates": "Actualizaciones", + "accuracy": "Precisión", + "highAccuracy": "Alta precisión", + "posAge": "Posición edad máxima", + "posTimeout": "Tiempo de espera de posición", + "zoom": "Zoom", + "enabled": "Activado", + "disabled": "Desactivado", + "marks": "Marcas" + }, + "notif": { + "geolocationError": "Tu navegador no implementa la API de geolocalización", + "newMarkerOutside": "Nuevo marcador fuera de tu rango", + "spotAdded": "Nuevo spot guardado en el mapa", + "spotNotAdded": "No se pudo agregar el spot", + "shopAdded": "Nueva tienda guardada en el mapa", + "shopNotAdded": "No se pudo agregar la tienda", + "barAdded": "Nueva barra guardada en el mapa", + "barNotAdded": "No se pudo agregar la barra", + "spotEdited": "¡Spot editado! Gracias", + "spotNotEdited": "No se pudo editar el spot", + "shopEdited": "¡Tienda editada! Gracias", + "shopNotEdited": "No se pudo editar la tienda", + "barEdited": "¡Barra editada! Gracias", + "barNotEdited": "No se pudo editar la tienda", + "spotDeleted": "Spot eliminado", + "spotNotDeleted": "No se pudo eliminar el spot", + "shopDeleted": "Tienda eliminada", + "shopNotDeleted": "No se pudo eliminar la tienda", + "barDeleted": "Barra eliminada", + "barNotDeleted": "Barra eliminada", + "markNameEmpty": "No especificó un nombre para la marca", + "markTypeEmpty": "No especificó un tipo para la marca", + "markRateEmpty": "No especificaste una nota para la marca", + "markPriceEmpty": "No especificaste un precio para la marca", + "lockFocusOn": "Centrar y bloquear la vista en su posición", + "unlockFocusOn": "Finalizó el bloqueo de posición", + "uploadPPSuccess": "Imagen de perfil actualizada", + "uploadPPFailed": "No se pudo cargar la foto de perfil", + "welcomeBack": "Encantado de verte de nuevo en BeerCrackerz!" + }, + "nav": { + "add": "Agregar", + "edit": "Editar", + "upload": "Subir", + "cancel": "Cancelar", + "close": "Cerca", + "delete": "Borrar" + }, + "map": { + "newTitle": "Nuevo marcador", + "newSpot": "Agregar un spot", + "newShop": "Añadir una tienda", + "newBar": "Añadir una barra", + "planLayerOSM": "Mapa OSM", + "satLayerEsri": "Satélite ESRI" + }, + "spot": { + "addTitle": "Nuevo spot", + "editTitle": "Editar spot", + "subtitle": "¡Un spot es un lugar extraordinario para tomar una cerveza! ¡Compártalo con la comunidad, ya sea por la vista asombrosa o por lo que sea que sea agradable para beber una cerveza!", + "nameLabel": "Nombra ese spot", + "descLabel": "¿Por qué no lo describe?", + "rateLabel": "Dale una nota", + "typeLabel": "Que tipo de spot es", + "forestType": "Bosque", + "riverType": "Río", + "lakeType": "Lago", + "cliffType": "Acantilado", + "mountainType": "Montaña", + "beachType": "Playa", + "seaType": "Mar", + "cityType": "Ciudad", + "povType": "Mirador", + "modifiersLabel": "Tiene alguna caracteristica especifica", + "benchModifier": "Banco", + "coveredModifier": "Cubierto", + "toiletModifier": "Inodoro", + "storeModifier": "Tienda cercana", + "trashModifier": "Basura", + "parkingModifier": "Estacionamiento" + }, + "shop": { + "addTitle": "Nueva tienda", + "editTitle": "Editar tienda", + "subtitle": "El lugar imprescindible para rellenar tu stock de cerveza. ¡Cuanta más información proporciones, mejor ayudarás a tus compañeros BeerCrackerz!", + "nameLabel": "Nombre de tienda", + "descLabel": "¿Por qué no lo describe?", + "rateLabel": "Dale una nota", + "priceLabel": "Que caro es", + "typeLabel": "Que tipo de tienda es", + "storeType": "Tienda de comestibles", + "superType": "Supermercado", + "hyperType": "Hipermercado", + "cellarType": "Cava", + "modifiersLabel": "Tiene alguna caracteristica especifica", + "bioModifier": "Bio", + "craftModifier": "Artesanal", + "freshModifier": "Refrigerado", + "cardModifier": "Tarjeta de crédito", + "choiceModifier": "Amplia selección" + }, + "bar": { + "addTitle": "Nueva barra", + "editTitle": "Editar barra", + "subtitle": "¡Un bar es un lugar sagrado donde puedes tomar unas cervezas de barril bien frías!", + "nameLabel": "Nombre de la barra", + "descLabel": "¿Por qué no lo describe?", + "rateLabel": "Dale una nota", + "priceLabel": "Que caro es", + "typeLabel": "Que tipo de barra es", + "regularType": "Regular", + "snackType": "Merienda", + "cellarType": "Cervecería", + "rooftopType": "Techo", + "modifiersLabel": "Tiene alguna caracteristica especifica", + "tobaccoModifier": "Cigarrillos", + "foodModifier": "Alimento", + "cardModifier": "Tarjeta de crédito", + "choiceModifier": "Amplia selección", + "outdoorModifier": "Exterior" + }, + "modal": { + "userTitle": "Cuenta de usuario", + "userAccuracyPref": "Alta precisión", + "darkThemePref": "Tema oscuro", + "userDebugPref": "Interfaz de depuración", + "startupHelp": "Empezando", + "langPref": "Lenguaje de interfaz", + "langFr": "🇫🇷 Francés", + "langEn": "🇬🇧 Inglés", + "langEs": "🇪🇸 Español", + "langDe": "🇩🇪 Alemán", + "langIt": "🇩🇪 Italiano", + "langPt": "🇵🇹 Portugués", + "aboutTitle": "Acerca de BeerCrackerz", + "aboutDesc": "¡Una idea brillante de David Béché!
        BeerCrackerz es la comunidad de amantes de la cerveza, llena de cazadores de pintas y amantes de los cereales.

        Beercrackerz – v0.1.0 – MBP 2022/2023", + "updatePP": "Actualiza tu foto de perfil", + "updatePPSizeError": "Seleccione una imagen debajo de 2.5 Mo", + "updatePPDimensionError": "El tamaño mínimo de la imagen es 512x512", + "updatePPTitle": "Nueva foto de perfil foto", + "updatePPDesc": "¡Recorta la imagen si es necesario y listo!", + "logout": "Desconectarse", + "hideShowTitle": "Opciones de mapa", + "hideShowSpots": "Spots", + "hideShowShops": "Tiendas", + "hideShowBars": "Barras", + "hideShowHelperLabel": "Haga clic en un icono para mostrar su descripción", + "spotHelperHideShow": "Para ocultar o mostrar todos los spots del mapa.", + "shopHelperHideShow": "Para ocultar o mostrar todas las tiendas en el mapa.", + "barHelperHideShow": "Para ocultar o mostrar todas las barras del mapa.", + "deleteMarkTitle": "Borrar marca", + "deleteMarkDesc": "¿Está seguro de que desea eliminar esta marca?
        Esta acción es permanente y no se puede revertir.", + "spotEditTitle": "Editar spot", + "shopEditTitle": "Editar tienda", + "barEditTitle": "Editar barra", + "helpTitle": "Descubre BeerCrackerz", + "helpNavNext": "¡Navegue a la página siguiente o salga de este menú de ayuda!", + "helpNavNextPrev": "¡Navegue a la página siguiente o anterior, o salga de este menú de ayuda!", + "helpNavEnd": "¡Ya puede cerrar este asistente!", + "helpPage1": "¡Bienvenido a BeerCrackerz!
        Veamos cómo funciona el mapa; su posición está marcada por el símbolo \"user-position\". Alrededor de su posición, un círculo azul representa la precisión actual de su posición. A tu alrededor, también hay un círculo dorado ; esta es la distancia máxima por la que puedes declarar un nuevo lugar, una tienda o un bar.", + "helpPage2": "Un spot está representado por el símbolo \"spot-mark\". Los spots son lugares perfectos para disfrutar de una lata de cerveza. Cada spot tiene sus especificidades, depende de ti descubrirlas.", + "helpPage3": "Una tienda está representada por el símbolo \"shop-mark\". Las tiendas son los lugares ideales para abastecerse de latas de cerveza. Cada tienda tiene sus especificidades, depende de ti descubrirlas.", + "helpPage4": "Una tienda barra representada por el símbolo \"bar-mark\". Los bares son lugares acogedores que sirven cervezas frescas y buenas. Cada barra tiene sus especificidades, depende de ti descubrirlas.", + "helpPage5": "Hay varios comandos disponibles: 1. elección del mapa base, 2. volver a centrar y bloquear el mapa en su posición, 3. mostrar u ocultar elementos del mapa y 4. su cuenta.", + "helpQuit": "Sal", + "helpQuitNoSee": "Sal y no vuelvas a ver" + }, + "popup": { + "spotFoundBy": "Un spot descubierto por", + "spotFoundWhen": "Desde el", + "shopFoundBy": "Una tienda añadida por", + "shopFoundWhen": "Desde el", + "barFoundBy": "Una barra añadida por", + "barFoundWhen": "Desde el", + "spotNoDesc": "No hay descripción disponible para este spot", + "shopNoDesc": "No hay descripción disponible para esta tienda", + "barNoDesc": "No hay descripción disponible para este bar" + }, + "auth": { + "login": { + "headTitle": "Conexión | BeerCrackerz", + "subtitle": "Conectarse", + "hiddenError": "¡Vaya! ¡Un texto oculto!", + "username": "Nombre de usuario", + "password": "Contraseña", + "login": "Conectarse", + "notRegistered": "¿Aún no estás registrado?", + "register": "Crear una cuenta", + "forgot": "¿Contraseña olvidada?", + "reset": "Reinicialo", + "bothEmpty": "Por favor ingrese un nombre de usuario y contraseña", + "usernameEmpty": "Por favor, ingrese un nombre de usuario", + "passwordEmpty": "Por favor inserte su contraseña", + "credsInvalid": "Nombre de usuario o contraseña incorrectos", + "serverError": "Se ha producido un error en el servidor, póngase en contacto con el soporte", + "checkMail": "Se ha enviado un correo electrónico para que pueda continuar" + }, + "register": { + "headTitle": "Inscribirse | BeerCrackerz", + "subtitle": "Inscribirse", + "hiddenError": "¡Vaya! ¡Un texto oculto!", + "username": "Nombre del usuario", + "mail": "Correo electrónico", + "password1": "Contraseña", + "password2": "Confirmar la contraseña", + "register": "Inscribirse", + "notRegistered": "¿Ya inscrito?", + "login": "Conectarse", + "fieldEmpty": "Por favor complete todos los campos del formulario", + "notMatchingPassword": "Las dos contraseñas no coinciden", + "activationSuccess": "¡Su cuenta ha sido activada exitosamente!", + "activationError": "Error al activar tu cuenta" + }, + "forgotPassword": { + "headTitle": "Contraseña olvidada | BeerCrackerz", + "subtitle": "Contraseña olvidada", + "hiddenError": "¡Vaya! ¡Un texto oculto!", + "mail": "Correo electrónico", + "submit": "Restablecer su contraseña", + "loginLabel": "¿Tu recuerdas?", + "login": "Conectarse", + "fieldEmpty": "Ingrese su dirección de correo electrónico", + "serverError": "Se ha producido un error en el servidor, póngase en contacto con el soporte" + }, + "resetPassword": { + "headTitle": "Restablecer la contraseña | BeerCrackerz", + "subtitle": "Restablecer la contraseña", + "hiddenError": "¡Vaya! ¡Un texto oculto!", + "password1": "Contraseña", + "password2": "Confirmar la contraseña", + "reset": "Restablecer su contraseña", + "loginLabel": "¿Tu recuerdas?", + "login": "Conectarse", + "fieldEmpty": "Por favor complete todos los campos del formulario", + "notMatchingPassword": "Las dos contraseñas no coinciden", + "serverError": "Se ha producido un error en el servidor, póngase en contacto con el soporte" + } + } +} diff --git a/static/nls/fr.json b/static/nls/fr.json new file mode 100644 index 0000000..ee4c221 --- /dev/null +++ b/static/nls/fr.json @@ -0,0 +1,244 @@ +{ + "debug": { + "lat": "Latitude", + "lng": "Longitude", + "updates": "Mises à jours", + "accuracy": "Précision", + "highAccuracy": "Haute précision", + "posAge": "Durée maximale de la position", + "posTimeout": "Durée d'échance de la position", + "zoom": "Zoom", + "enabled": "Activé", + "disabled": "Désactivé", + "marks": "Points" + }, + "notif": { + "geolocationError": "Votre navigateur ne suporte pas l'API de geolocalisation", + "newMarkerOutside": "Nouveau point hors de votre porté", + "spotAdded": "Nouveau spot ajouté à la carte", + "spotNotAdded": "Impossible d'ajouter le spot", + "shopAdded": "Nouveau magasin ajouté à la carte", + "shopNotAdded": "Impossible d'ajouter le magasin", + "barAdded": "Nouveau bar ajouté à la carte", + "barNotAdded": "Impossible d'ajouter le bar", + "spotEdited": "Spot édité! Merci", + "spotNotEdited": "Impossible d'éditer le spot", + "shopEdited": "Magasin édité! Merci", + "shopNotEdited": "Impossible d'éditer le magasin", + "barEdited": "Bar édité! Merci", + "barNotEdited": "Impossible d'éditer le bar", + "spotDeleted": "Spot supprimé", + "spotNotDeleted": "Impossible de supprimer le spot", + "shopDeleted": "Magasin supprimé", + "shopNotDeleted": "Impossible de supprimer le magasin", + "barDeleted": "Bar supprimé", + "barNotDeleted": "Impossible de supprimer le bar", + "markNameEmpty": "Préciser un nom pour le point", + "markTypeEmpty": "Préciser un type pour le point", + "markRateEmpty": "Donnez une note pour le point", + "markPriceEmpty": "Donnez un prix pour le point", + "lockFocusOn": "Centrer et verrouiller sur la position", + "unlockFocusOn": "Fin du verrouillage de la position", + "uploadPPSuccess": "Photo de profil téléversée avec succès", + "uploadPPFailed": "Impossible de téléverser la photo de profil", + "welcomeBack": "Content de vous revoir sur BeerCrackerz!" + }, + "nav": { + "add": "Ajouter", + "edit": "Éditer", + "upload": "Téléverser", + "cancel": "Annuler", + "close": "Fermer", + "delete": "Supprimer" + }, + "map": { + "newTitle": "Nouveau point", + "newSpot": "Ajouter un spot", + "newShop": "Ajouter un magasin", + "newBar": "Ajouter un bar", + "planLayerOSM": "Plan OSM", + "satLayerEsri": "Satellite ESRI" + }, + "spot": { + "addTitle": "Nouveau spot", + "editTitle": "Éditer le spot", + "subtitle": "Un spot est un endroit remarquable pour se craquer une bière ! Partager le avec la communauté, que ce soit pour sa vue ou pour n'importe quelle autre raison, en faisant l'endroit parfait pour y boire une bière!", + "nameLabel": "Nommer ce spot", + "descLabel": "Pourquoi ne pas le décrire une peu", + "rateLabel": "Lui attribuer une note", + "typeLabel": "Quel est le type du spot", + "forestType": "Fôret", + "riverType": "Rivière", + "lakeType": "Lac", + "cliffType": "Falaise", + "mountainType": "Montagne", + "beachType": "Plage", + "seaType": "Mer", + "cityType": "Ville", + "povType": "Point de vue", + "modifiersLabel": "Caractéristiques particulières au spot", + "benchModifier": "Banc", + "coveredModifier": "Couvert", + "toiletModifier": "Toilette", + "storeModifier": "Magasin à proximité", + "trashModifier": "Poubelle", + "parkingModifier": "Parking" + }, + "shop": { + "addTitle": "Nouveau magasin", + "editTitle": "Éditer le magasin", + "subtitle": "L'endroit parfait pour refaire le stock de bière. Le plus d'information vous fournissez, le plus d'amateurs du craquage de bière seront satisfaits!", + "nameLabel": "Nom du magasin", + "descLabel": "Pourquoi ne pas le décrire une peu", + "rateLabel": "Lui attribuer une note", + "priceLabel": "Est-il cher", + "typeLabel": "Quel est le type du magasin", + "storeType": "Épicerie", + "superType": "Supermarché", + "hyperType": "Hypermarché", + "cellarType": "Cave", + "modifiersLabel": "Caractéristiques particulières au magasin", + "bioModifier": "Bio", + "craftModifier": "Artisanal", + "freshModifier": "Réfrigéré", + "cardModifier": "Carte de crédit", + "choiceModifier": "Large choix" + }, + "bar": { + "addTitle": "Nouveau bar", + "editTitle": "Éditer le bar", + "subtitle": "Un bar est le sain endroit ou vous pouvez obtenir le breuvage des dieux à la pression!", + "nameLabel": "Nom du bar", + "descLabel": "Pourquoi ne pas le décrire une peu", + "rateLabel": "Lui attribuer une note", + "priceLabel": "Est-il cher", + "typeLabel": "Quel est le type du bar", + "regularType": "Classique", + "snackType": "Snack", + "cellarType": "Brasserie", + "rooftopType": "Rooftop", + "modifiersLabel": "Caractéristiques particulières au bar", + "tobaccoModifier": "Cigarettes", + "foodModifier": "Nourriture", + "cardModifier": "Carte de crédit", + "choiceModifier": "Large choix", + "outdoorModifier": "Extérieur" + }, + "modal": { + "userTitle": "Compte utilisateur", + "userAccuracyPref": "Haute précision", + "darkThemePref": "Theme sombre", + "userDebugPref": "Interface de debug", + "startupHelp": "Aide au démarrage", + "langPref": "Langue de l'interface", + "langFr": "🇫🇷 Français", + "langEn": "🇬🇧 Anglais", + "langEs": "🇪🇸 Espagnol", + "langDe": "🇩🇪 Allemand", + "langIt": "🇩🇪 Italien", + "langPt": "🇵🇹 Portuguais", + "aboutTitle": "À propos de BeerCrackerz", + "aboutDesc": "Un idée brillant du grand David Béché!
        BeerCrackerz, c'est la communauté des amoureux de la bière et du plein air, des pourfendeurs de pinte, des déglingos de la céréale.

        Beercrackerz – v0.1.0 – MBP 2022/2023", + "updatePP": "Mettre à jour la photo de profil", + "updatePPSizeError": "Le poid de l'image doit être inférieur à 2.5 Mo", + "updatePPDimensionError": "La taille minimale de l'image est de 512x512", + "updatePPTitle": "Nouvelle photo de profil", + "updatePPDesc": "Rogner l'image selon vos besoin et c'est parti!", + "logout": "Se déconnecter", + "hideShowTitle": "Options de la carte", + "hideShowSpots": "Spots", + "hideShowShops": "Magasins", + "hideShowBars": "Bars", + "hideShowHelperLabel": "Cliquer sur un icône pour en afficher la description", + "spotHelperHideShow": "Pour cacher ou aficher tout les spots de la carte.", + "shopHelperHideShow": "Pour cacher ou aficher tout les magasins de la carte.", + "barHelperHideShow": "Pour cacher ou aficher tout les bars de la carte.", + "deleteMarkTitle": "Supprimer le point", + "deleteMarkDesc": "Ètes vous sûr de vouloir supprimer ce point?
        Cette action est permanente et irréversible.", + "spotEditTitle": "Éditer le spot", + "shopEditTitle": "Éditer le magasin", + "barEditTitle": "Éditer le bar", + "helpTitle": "Découvrir BeerCrackerz", + "helpNavNext": "Naviguez jusqu'à la page suivante, ou quittez ce menu d'aide!", + "helpNavNextPrev": "Naviguez jusqu'à la page suivante ou précèdante, ou quittez ce menu d'aide!", + "helpNavEnd": "Vous pouvez maintenant fermer cet assistant!", + "helpPage1": "Bienvenue sur BeerCrackerz!
        Voyons comment marche la carte ; votre position est marquée par le symbole \"user-position\". Autour de votre position, un cercle bleu ; ce dernier représente la précision actuelle de votre position. Autour de vous, il y a également un cercle doré ; c'est la distance maximale pour laquelle vous pouvez déclarer un nouveau spot, un magasin ou un bar.", + "helpPage2": "Un spot est représenté par le symbole \"spot-mark\". Les spots sont les endroit ideaux pour deguster une cannette. Chaque spot à ses specificité, a vous de les découvrir.", + "helpPage3": "Un magasin est représenté par le symbole \"shop-mark\". Les spots sont les endroit ideaux pour se ravitailler en cannettes. Chaque magasin à ses specificités, à vous de les découvrir.", + "helpPage4": "Un bar est représenté par le symbole \"bar-mark\". Les bars sont des endroit conviviaux qui servent de fraîche et bonnes bières. Chaque bar à ses specificités, à vous de les découvrir.", + "helpPage5": "Plusieurs commandes sont à votre disposition : 1. choix du fond de carte, 2. recentrer et vérouiller la carte sur votre position, 3. afficher ou cacher les éléments de la carte et 4. votre compte.", + "helpQuit": "Quitter", + "helpQuitNoSee": "Quitter et ne plus revoir" + }, + "popup": { + "spotFoundBy": "Un spot découvert par", + "spotFoundWhen": "Depuis le", + "shopFoundBy": "Un magasin ajouté par", + "shopFoundWhen": "Depuis le", + "barFoundBy": "Un bar ajouté par", + "barFoundWhen": "Depuis le", + "spotNoDesc": "Pas de description disponible pour ce spot", + "shopNoDesc": "Pas de description disponible pour ce magasin", + "barNoDesc": "Pas de description disponible pour ce bar" + }, + "auth": { + "login": { + "headTitle": "Connexion | BeerCrackerz", + "subtitle": "Se connecter", + "hiddenError": "Oh! Un texte caché!", + "username": "Nom d'utilisateur", + "password": "Mot de passe", + "login": "Se connecter", + "notRegistered": "Pas encore inscrit?", + "register": "Créer un compte", + "forgot": "Mot de passe oublié?", + "reset": "Le réinitialiser", + "bothEmpty": "Veuillez saisir un nom d'utilisateur et un mot de passe", + "usernameEmpty": "Veuillez saisir un nom d'utilisateur", + "passwordEmpty": "Veuillez saisir votre mot de passe", + "credsInvalid": "Nom d'utilisateur ou mot de passe incorrect", + "serverError": "Une erreur serveur est survenue, contactez le support", + "checkMail": "Consultez vos email avant de continuer" + }, + "register": { + "headTitle": "S'inscrire | BeerCrackerz", + "subtitle": "S'inscrire", + "hiddenError": "Oh! Un texte caché!", + "username": "Nom d'utilisateur", + "mail": "Adresse email", + "password1": "Mot de passe", + "password2": "Confirmer le mot de passe", + "register": "S'inscrire", + "notRegistered": "Déjà inscrit?", + "login": "Se connecter", + "fieldEmpty": "Veuillez remplir tous les champs du formulaire", + "notMatchingPassword": "Les deux mots de passe ne correspondent pas", + "activationSuccess": "Votre compte à été activé avec succès!", + "activationError": "Erreur lors de l'activation de votre compte" + }, + "forgotPassword": { + "headTitle": "Mot de passe oublié | BeerCrackerz", + "subtitle": "Mot de passe oublié", + "hiddenError": "Oh! Un texte caché!", + "mail": "Mail", + "submit": "Réinitialiser votre mot de passe", + "loginLabel": "Vous vous en souvenez?", + "login": "Se connecter", + "fieldEmpty": "Renseignez votre adresse mail", + "serverError": "Une erreur serveur est survenue, contactez le support" + }, + "resetPassword": { + "headTitle": "Réinitialiser le mot de passe | BeerCrackerz", + "subtitle": "Réinitialiser le mot de passe", + "hiddenError": "Oh! Un texte caché!", + "password1": "Mot de passe", + "password2": "Confirmer le mot de passe", + "reset": "Réinitialiser votre mot de passe", + "loginLabel": "Vous vous en souvenez?", + "login": "Se connecter", + "fieldEmpty": "Veuillez remplir tous les champs du formulaire", + "notMatchingPassword": "Les deux mots de passe ne correspondent pas", + "serverError": "Une erreur serveur est survenue, contactez le support" + } + } +} diff --git a/static/nls/it.json b/static/nls/it.json new file mode 100644 index 0000000..5a360ce --- /dev/null +++ b/static/nls/it.json @@ -0,0 +1,244 @@ +{ + "debug": { + "lat": "Latitudine", + "lng": "Longitudine", + "updates": "Aggiornamenti", + "accuracy": "Precisione", + "highAccuracy": "Alta precisione", + "posAge": "Posizione età massima", + "posTimeout": "Scadenza posizione", + "zoom": "Ingrandisci", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "marks": "Marcatore" + }, + "notif": { + "geolocationError": "Il tuo browser non implementa l'API di geolocalizzazione", + "newMarkerOutside": "Nuovo marcatore fuori dalla tua portata", + "spotAdded": "Nuovo spot salvato sulla mappa", + "spotNotAdded": "Impossibile aggiungere lo spot", + "shopAdded": "Nuovo negozio salvato sulla mappa", + "shopNotAdded": "Impossibile aggiungere il negozio", + "barAdded": "Nuova barra salvata sulla mappa", + "barNotAdded": "Impossibile aggiungere la barra", + "spotEdited": "Spot modificato! Grazie", + "spotNotEdited": "Impossibile modificare il spot", + "shopEdited": "Negozio modificato! Grazie", + "shopNotEdited": "Impossibile modificare il negozio", + "barEdited": "Barra modificata! Grazie", + "barNotEdited": "Impossibile modificare la barra", + "spotDeleted": "Spot eliminato", + "spotNotDeleted": "Impossibile eliminare lo spot", + "shopDeleted": "Negozio eliminato", + "shopNotDeleted": "Impossibile eliminare il negozio", + "barDeleted": "Barra eliminata", + "barNotDeleted": "Impossibile eliminare la barra", + "markNameEmpty": "Non hai specificato un nome per il marcatore", + "markTypeEmpty": "Non hai specificato un tipo per il marcatore", + "markRateEmpty": "Non hai specificato una valutazione per il marcatore", + "markPriceEmpty": "Non hai specificato un prezzo per il marcatore", + "lockFocusOn": "Centratura e blocco vista sulla tua posizione", + "unlockFocusOn": "Blocco della posizione terminato", + "uploadPPSuccess": "Immagine del profilo aggiornata", + "uploadPPFailed": "Impossibile caricare l'immagine del profilo", + "welcomeBack": "Felice di rivederti su BeerCrackerz!" + }, + "nav": { + "add": "Aggiungere", + "edit": "Modificare", + "upload": "Caricamento", + "cancel": "Annulla", + "close": "Vicino", + "delete": "Eliminare" + }, + "map": { + "newTitle": "Nuovo marcatore", + "newSpot": "Aggiungi un spot", + "newShop": "Aggiungi un negozio", + "newBar": "Aggiungi una barra", + "planLayerOSM": "Carte OSM", + "satLayerEsri": "Satellitare ESRI" + }, + "spot": { + "addTitle": "Nuovo spot", + "editTitle": "Modifica spot", + "subtitle": "Un spot è un posto straordinario per rompere una birra! Condividilo con la community, che sia per la vista mozzafiato o per quello che è piacevole bere una birra!", + "nameLabel": "Dai un nome a quel spot", + "descLabel": "Perché non lo descrivi", + "rateLabel": "Dagli una nota", + "typeLabel": "Che tipo di spot è", + "forestType": "Foresta", + "riverType": "Fiume", + "lakeType": "Lago", + "cliffType": "Scogliera", + "mountainType": "Montagna", + "beachType": "Spiaggia", + "seaType": "Mare", + "cityType": "Città", + "povType": "Punto di vista", + "modifiersLabel": "Ha delle caratteristiche specifiche", + "benchModifier": "Panca", + "coveredModifier": "Coperto", + "toiletModifier": "Toilette", + "storeModifier": "Negozio nelle vicinanze", + "trashModifier": "Spazzatura", + "parkingModifier": "Parcheggio" + }, + "shop": { + "addTitle": "Nuovo negozio", + "editTitle": "Modifica negozio", + "subtitle": "The must have place to refill your beer stock. The more info you provide, the better you help your fellow beercrackerz!", + "nameLabel": "Nome del negozio", + "descLabel": "Perché non lo descrivi", + "rateLabel": "Dagli una nota", + "priceLabel": "Quanto costa", + "typeLabel": "Che tipo di negozio è", + "storeType": "Negozio di alimentari", + "superType": "Supermercato", + "hyperType": "Ipermercato", + "cellarType": "Cantina", + "modifiersLabel": "Ha delle caratteristiche specifiche", + "bioModifier": "Bio", + "craftModifier": "Artigianale", + "freshModifier": "Refrigerato", + "cardModifier": "Carta di credito", + "choiceModifier": "Ampia scelta" + }, + "bar": { + "addTitle": "Nuova barra", + "editTitle": "Modifica barra", + "subtitle": "Un bar è un luogo sacro dove puoi prendere delle birre alla spina ben fredde!", + "nameLabel": "Nome della barra", + "descLabel": "Perché non lo descrivi", + "rateLabel": "Dagli una nota", + "priceLabel": "Quanto costa", + "typeLabel": "Che tipo di bar è", + "regularType": "Regolare", + "snackType": "Merenda", + "cellarType": "Birrificio", + "rooftopType": "Tetto", + "modifiersLabel": "Ha delle caratteristiche specifiche", + "tobaccoModifier": "Sigarette", + "foodModifier": "Cibo", + "cardModifier": "Carta di credito", + "choiceModifier": "Ampia scelta", + "outdoorModifier": "All'aperto" + }, + "modal": { + "userTitle": "Account utente", + "userAccuracyPref": "Alta precisione", + "darkThemePref": "Tema scuro", + "userDebugPref": "Interfaccia di debug", + "startupHelp": "Aiuto iniziale", + "langPref": "Linguaggio dell'interfaccia", + "langFr": "🇫🇷 Francese", + "langEn": "🇬🇧 Inglese", + "langEs": "🇪🇸 Spagnolo", + "langDe": "🇩🇪 Tedesco", + "langIt": "🇩🇪 Italiano", + "langPt": "🇵🇹 Portoghese", + "aboutTitle": "A proposito di BeerCrackerz", + "aboutDesc": "Un'idea geniale di David Béché!
        BeerCrackerz è la comunità degli amanti della birra, piena di cacciatori di pinte e amanti dei cereali.

        Beercrackerz – v0.1.0 – MBP 2022/2023", + "updatePP": "Aggiorna la tua immagine del profilo", + "updatePPSizeError": "Seleziona un'immagine inferiore a 2.5Mo", + "updatePPDimensionError": "La dimensione minima dell'immagine è 512x512", + "updatePPTitle": "Nuova immagine dell'immagine del profilo", + "updatePPDesc": "Ritaglia l'immagine se necessario e vai!", + "logout": "Disconnessione", + "hideShowTitle": "Opzioni mappa", + "hideShowSpots": "Spots", + "hideShowShops": "Negozi", + "hideShowBars": "Barre", + "hideShowHelperLabel": "Fare clic su un'icona per visualizzarne la descrizione", + "spotHelperHideShow": "Per nascondere o mostrare tutti i spots sulla mappa.", + "shopHelperHideShow": "Per nascondere o mostrare tutti i negozzi sulla mappa.", + "barHelperHideShow": "Per nascondere o mostrare tutte le barre sulla mappa.", + "deleteMarkTitle": "Elimina marcatore", + "deleteMarkDesc": "Sei sicuro di voler eliminare questo marchio?
        Questa azione è permanente e non può essere annullata.", + "spotEditTitle": "Modifica spot", + "shopEditTitle": "Modifica negozio", + "barEditTitle": "Modifica barra", + "helpTitle": "Scopri BeerCrackerz", + "helpNavNext": "Vai alla pagina successiva o esci da questo menu di aiuto!", + "helpNavNextPrev": "Vai alla pagina successiva o precedente o esci da questo menu di aiuto!", + "helpNavEnd": "Ora puoi chiudere questa procedura guidata!", + "helpPage1": "Benvenuto in BeerCrackerz!
        Vediamo come funziona la mappa; la tua posizione è contrassegnata dal simbolo \"user-position\". Intorno alla tua posizione, un cerchio blu rappresenta la precisione attuale della tua posizione. Intorno a te c'è anche un cerchio d'oro ; questa è la distanza massima per la quale puoi dichiarare un nuovo spot, un negozio o un bar.", + "helpPage2": "Un spot è rappresentato dal simbolo \"spot-mark\". I punti sono luoghi perfetti per godersi una lattina di birra. Ogni spot ha le sue specificità, sta a te scoprirle.", + "helpPage3": "Un negozio è rappresentato dal simbolo \"shop-mark\". I negozi sono i luoghi ideali per fare scorta di lattine di birra. Ogni negozio ha le sue specificità, sta a te scoprirle.", + "helpPage4": "Una barra è rappresentata dal simbolo \"bar-mark\". I bar sono luoghi accoglienti che servono birre fresche e buone. Ogni bar ha le sue specificità, sta a te scoprirle.", + "helpPage5": "Sono disponibili diversi comandi: 1. scelta della mappa di base, 2. ricentra e blocca la mappa sulla tua posizione, 3. mostra o nascondi gli elementi della mappa e 4. il tuo account.", + "helpQuit": "Quit", + "helpQuitNoSee": "Esci e non vedere più" + }, + "popup": { + "spotFoundBy": "Un spot scoperto da", + "spotFoundWhen": "Dal", + "shopFoundBy": "Un negozio aggiunto da", + "shopFoundWhen": "Dal", + "barFoundBy": "Una barra aggiunta da", + "barFoundWhen": "Dal", + "spotNoDesc": "Nessuna descrizione disponibile per questo spot", + "shopNoDesc": "Nessuna descrizione disponibile per questo negozio", + "barNoDesc": "Nessuna descrizione disponibile per questa barra" + }, + "auth": { + "login": { + "headTitle": "Login | BeerCrackerz", + "subtitle": "Login", + "hiddenError": "Oh! Un testo nascosto!", + "username": "Nome utente", + "password": "Password", + "login": "Login", + "notRegistered": "Non sei ancora registrato?", + "register": "Creare un account", + "forgot": "Ha dimenticato la password", + "reset": "Ripristinalo", + "bothEmpty": "Si prega di compilare i campi nome utente e password", + "usernameEmpty": "Si prega di compilare il campo del nome utente", + "passwordEmpty": "Si prega di compilare il campo della password", + "credsInvalid": "Credenziali non valide", + "serverError": "È successo qualcosa di sbagliato, contatta l'assistenza", + "checkMail": "È stata inviata un'e-mail in modo da poter procedere" + }, + "register": { + "headTitle": "Registrati | BeerCrackerz", + "subtitle": "Creare un account", + "hiddenError": "Oh! Un testo nascosto!", + "username": "Nome utente", + "mail": "Indirizzo e-mail", + "password1": "Password", + "password2": "Conferma password", + "register": "Registrati", + "notRegistered": "Già registrato??", + "login": "Login", + "fieldEmpty": "Si prega di compilare tutti i campi obbligatori", + "notMatchingPassword": "Le password non corrispondono", + "activationSuccess": "Il tuo account è stato creato con successo!", + "activationError": "È successo qualcosa di sbagliato durante la creazione del tuo account, contatta l'assistenza" + }, + "forgotPassword": { + "headTitle": "Ha dimenticato la password | BeerCrackerz", + "subtitle": "Ha dimenticato la password", + "hiddenError": "Oh! Un testo nascosto!", + "mail": "Email", + "submit": "Resetta la password", + "loginLabel": "Te lo ricordi?", + "login": "Login", + "fieldEmpty": "Si prega di compilare tutti i campi obbligatori", + "serverError": "È successo qualcosa di sbagliato, contatta l'assistenza" + }, + "resetPassword": { + "headTitle": "Resetta la password | BeerCrackerz", + "subtitle": "Resetta la password", + "hiddenError": "Oh! Un testo nascosto!", + "password1": "Nuova password", + "password2": "Conferma password", + "reset": "Resetta la password", + "loginLabel": "Te lo ricordi?", + "login": "Login", + "fieldEmpty": "Per favore compila tutti i campi", + "notMatchingPassword": "Le password non corrispondono", + "serverError": "È successo qualcosa di sbagliato, contatta l'assistenza" + } + } +} diff --git a/static/nls/pt.json b/static/nls/pt.json new file mode 100644 index 0000000..d53dd30 --- /dev/null +++ b/static/nls/pt.json @@ -0,0 +1,244 @@ +{ + "debug": { + "lat": "Latitude", + "lng": "Longitude", + "updates": "Atualizações", + "accuracy": "Precisão", + "highAccuracy": "Alta precisão", + "posAge": "Idade máxima da posição", + "posTimeout": "Tempo limite da posição", + "zoom": "Zoom", + "enabled": "Habilitado", + "disabled": "Desabilitado", + "marks": "Marcas" + }, + "notif": { + "geolocationError": "Seu navegador não implementa a API de geolocalização", + "newMarkerOutside": "Novo marcador fora do seu alcance", + "spotAdded": "Novo spot salvo no mapa", + "spotNotAdded": "Não foi possível adicionar o spot", + "shopAdded": "Nova loja salva no mapa", + "shopNotAdded": "Não foi possível adicionar a loja", + "barAdded": "Nova barra salva no mapa", + "barNotAdded": "Não foi possível adicionar a barra", + "spotEdited": "Spot editado! Obrigado", + "spotNotEdited": "Não foi possível editar o spot", + "shopEdited": "Loja editada! Obrigado", + "shopNotEdited": "Não foi possível editar a loja", + "barEdited": "Barra editada! Obrigado", + "barNotEdited": "Não foi possível editar a barra", + "spotDeleted": "Spot excluído", + "spotNotDeleted": "Não foi possível excluir o spot", + "shopDeleted": "Loja excluído", + "shopNotDeleted": "Não foi possível excluir a loja", + "barDeleted": "Barra excluído", + "barNotDeleted": "Não foi possível excluir a barra", + "markNameEmpty": "Você não especificou um nome para a marca", + "markTypeEmpty": "Você não especificou um tipo para a marca", + "markRateEmpty": "Você não especificou uma taxa para a marca", + "markPriceEmpty": "Você não especificou um preço para a marca", + "lockFocusOn": "Centralizando e bloqueando a visualização em sua posição", + "unlockFocusOn": "Bloqueio de posição finalizado", + "uploadPPSuccess": "Foto do perfil atualizada", + "uploadPPFailed": "Falha ao carregar a foto do perfil", + "welcomeBack": "Fico feliz em vê-lo de volta no BeerCrackerz!" + }, + "nav": { + "add": "Adicionar", + "edit": "Editar", + "upload": "Carregar", + "cancel": "Cancelar", + "close": "Perto", + "delete": "Excluir" + }, + "map": { + "newTitle": "Novo marcador", + "newSpot": "Adicionar um spot", + "newShop": "Adicionar uma loja", + "newBar": "Adicionar uma bara", + "planLayerOSM": "Plano OSM", + "satLayerEsri": "Satélite ESRI" + }, + "spot": { + "addTitle": "Novo spot", + "editTitle": "Editar spot", + "subtitle": "Um spot é um lugar notável para quebrar uma cerveja! Compartilhe com a comunidade, seja pela vista deslumbrante seja pelo prazer de beber uma cervejinha!", + "nameLabel": "Nomeie esse spot", + "descLabel": "Por que você não descreve", + "rateLabel": "Dê uma nota", + "typeLabel": "Que tipo de spot é", + "forestType": "Floresta", + "riverType": "Rio", + "cliffType": "Penhasco", + "lakeType": "Lago", + "mountainType": "Montanha", + "beachType": "Praia", + "seaType": "Mar", + "cityType": "Cidade", + "povType": "Ponto de vista", + "modifiersLabel": "Tem alguma característica específica", + "benchModifier": "Banco", + "coveredModifier": "Abordado", + "toiletModifier": "Toalete", + "storeModifier": "Loja próxima", + "trashModifier": "Lixo", + "parkingModifier": "Estacionamento" + }, + "shop": { + "addTitle": "Nova loja", + "editTitle": "Editar loja", + "subtitle": "O deve ter lugar para reabastecer seu estoque de cerveja. Quanto mais informações você fornecer, melhor você ajudará seus colegas crackerz!", + "nameLabel": "Nome da loja", + "descLabel": "Por que você não descreve", + "rateLabel": "Dê uma nota", + "priceLabel": "Quão caro é", + "typeLabel": "Que tipo de loja é", + "storeType": "Bomboneria", + "superType": "Supermercado", + "hyperType": "Hipermercado", + "cellarType": "Porão", + "modifiersLabel": "Tem alguma característica específica", + "bioModifier": "Bio", + "craftModifier": "Artesanal", + "freshModifier": "Refrigerado", + "cardModifier": "Cartão de crédito", + "choiceModifier": "Ampla escolha" + }, + "bar": { + "addTitle": "Nova barra", + "editTitle": "Editar barra", + "subtitle": "Um bar é um lugar sagrado onde você pode tomar alguns chopes bem gelados!", + "nameLabel": "Nome do bar", + "descLabel": "Por que você não descreve", + "rateLabel": "Dê uma nota", + "priceLabel": "Quão caro é", + "typeLabel": "Que tipo de bara é", + "regularType": "Regular", + "snackType": "Lanche", + "cellarType": "Cervejaria", + "rooftopType": "Cobertura", + "modifiersLabel": "Tem alguma característica específica", + "tobaccoModifier": "Cigarros", + "foodModifier": "Comida", + "cardModifier": "Cartão de crédito", + "choiceModifier": "Ampla escolha", + "outdoorModifier": "Terraço" + }, + "modal": { + "userTitle": "Conta de usuário", + "userAccuracyPref": "Alta precisão", + "darkThemePref": "Tema escuro", + "userDebugPref": "Interface de depuração", + "startupHelp": "Começando", + "langPref": "Idioma da interface", + "langFr": "🇫🇷 Francês", + "langEn": "🇬🇧 Inglês", + "langEs": "🇪🇸 Espanhol", + "langDe": "🇩🇪 Alemão", + "langIt": "🇩🇪 Italiano", + "langPt": "🇵🇹 Português", + "aboutTitle": "Sobre BeerCrackerz", + "aboutDesc": "Uma ideia brilhante de David Béché!
        BeerCrackerz é a comunidade dos amantes da cerveja, cheia de caçadores de cerveja e amantes de cereais.

        Beercrackerz – v0.1.0 – MBP 2022/2023", + "updatePP": "Atualize sua foto de perfil", + "updatePPSizeError": "Selecione uma imagem abaixo de 2,5 Mo", + "updatePPDimensionError": "O tamanho mínimo da imagem é 512x512", + "updatePPTitle": "Nova foto de perfil", + "updatePPDesc": "Recorte a imagem, se necessário, e pronto!", + "logout": "Sair", + "hideShowTitle": "Opções do mapa", + "hideShowSpots": "Spots", + "hideShowShops": "Lojas", + "hideShowBars": "Bares", + "hideShowHelperLabel": "Clique em um ícone para exibir sua descrição", + "spotHelperHideShow": "Para ocultar ou mostrar todos os spots no mapa.", + "shopHelperHideShow": "Para ocultar ou mostrar todas as lojas no mapa.", + "barHelperHideShow": "Para ocultar ou mostrar todas as barras no mapa.", + "deleteMarkTitle": "Excluir marca", + "deleteMarkDesc": "Tem certeza de que deseja excluir esta marca?
        Esta ação é permanente e não pode ser revertida.", + "spotEditTitle": "Editar spot", + "shopEditTitle": "Editar loja", + "barEditTitle": "Editar barra", + "helpTitle": "Descubra o BeerCrackerz", + "helpNavNext": "Navegue para a próxima página ou saia deste menu de ajuda!", + "helpNavNextPrev": "Navegue para a página seguinte ou anterior ou saia deste menu de ajuda!", + "helpNavEnd": "Agora você pode fechar este assistente!", + "helpPage1": "Bem-vindo ao BeerCrackerz!
        Vamos ver como o mapa funciona; sua posição é marcada pelo símbolo \"user-position\". Ao redor de sua posição, um círculo azul representa a precisão atual de sua posição. Ao seu redor, há também um círculo dourado ; esta é a distância máxima para a qual você pode declarar um novo spot, uma loja ou um bar.", + "helpPage2": "Um spot é representado pelo símbolo \"spot-mark\". Spots são lugares perfeitos para curtir uma lata de cerveja. Cada spot tem as suas especificidades, cabe-te a ti descobri-las.", + "helpPage3": "Uma loja é representada pelo símbolo \"shop-mark\". As lojas são os locais ideais para estocar latas de cerveja. Cada loja tem suas especificidades, cabe a você descobri-las.", + "helpPage4": "Uma barra é representada pelo símbolo \"bar-mark\". Bares são lugares amigáveis ​​que servem cervejas frescas e boas. Cada barra tem suas especificidades, cabe a você descobri-las.", + "helpPage5": "Vários comandos estão disponíveis: 1. escolha do mapa base, 2. recentralizar e bloquear o mapa em sua posição, 3. mostrar ou ocultar itens do mapa e 4. sua conta.", + "helpQuit": "Saia", + "helpQuitNoSee": "Saia e não veja mais" + }, + "popup": { + "spotFoundBy": "Um spot descoberto por", + "spotFoundWhen": "Desde o", + "shopFoundBy": "Uma loja adicionada por", + "shopFoundWhen": "Desde o", + "barFoundBy": "Uma barra adicionada por", + "barFoundWhen": "Desde o", + "spotNoDesc": "Nenhuma descrição disponível para este spot", + "shopNoDesc": "Nenhuma descrição disponível para esta loja", + "barNoDesc": "Nenhuma descrição disponível para esta barra" + }, + "auth": { + "login": { + "headTitle": "Conexão | BeerCrackerz", + "subtitle": "Se conector", + "hiddenError": "Oh! Um texto oculto!", + "username": "Nome de usuário", + "password": "Senha", + "login": "Entrar", + "notRegistered": "Não registrado?", + "register": "Crie a sua conta aqui", + "forgot": "Esqueceu sua senha?", + "reset": "Reinicie", + "bothEmpty": "Por favor, insira um nome de usuário e senha", + "usernameEmpty": "Por favor coloque um nome de usuário", + "passwordEmpty": "Por favor, insira sua senha", + "credsInvalid": "Nome de utilizador ou palavra-passe incorrectos", + "serverError": "Ocorreu um erro no servidor, entre em contato com o suporte", + "checkMail": "Um e-mail foi enviado para que você possa prosseguir" + }, + "register": { + "headTitle": "Registro | BeerCrackerz", + "subtitle": "Registro", + "hiddenError": "Oh! Um texto oculto!", + "username": "Nome do usuário", + "mail": "Endereço de e-mail", + "password1": "Senha", + "password2": "Confirme a Senha", + "register": "Registro", + "notRegistered": "Já registrado?", + "login": "Entrar", + "fieldEmpty": "Por favor, preencha todos os campos do formulário", + "notMatchingPassword": "As duas senhas não combinam", + "activationSuccess": "Sua conta foi ativada com sucesso!", + "activationError": "Erro ao ativar sua conta" + }, + "forgotPassword": { + "headTitle": "Esqueceu sua senha | BeerCrackerz", + "subtitle": "Esqueceu sua senha", + "hiddenError": "Oh! Um texto oculto!", + "mail": "O email", + "submit": "Redefina sua senha", + "loginLabel": "Você lembra?", + "login": "Entrar", + "fieldEmpty": "Insira o seu endereço de email", + "serverError": "Ocorreu um erro no servidor, entre em contato com o suporte" + }, + "resetPassword": { + "headTitle": "Redefinir senha | BeerCrackerz", + "subtitle": "Redefinir senha", + "hiddenError": "Oh! Um texto oculto!", + "password1": "Senha", + "password2": "Confirme a Senha", + "reset": "Redefina sua senha", + "loginLabel": "Você lembra?", + "login": "Entrar", + "fieldEmpty": "Por favor, preencha todos os campos do formulário", + "notMatchingPassword": "As duas senhas não combinam", + "serverError": "Ocorreu um erro no servidor, entre em contato com o suporte" + } + } +}