diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..472150eeb6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +src/test.ts \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000000..ff3f3e8f9b --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,27 @@ +/* eslint-env node */ +module.exports = { + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:svelte/recommended", + ], + parser: "@typescript-eslint/parser", + parserOptions: { + extraFileExtensions: [".svelte"], + }, + overrides: [ + { + files: ["*.svelte"], + parser: "svelte-eslint-parser", + parserOptions: { + parser: "@typescript-eslint/parser", + }, + }, + ], + plugins: ["@typescript-eslint"], + root: true, + env: { + browser: true, + node: true, + }, +} diff --git a/.github/actions/setup-and-validate/action.yml b/.github/actions/setup-and-validate/action.yml index ae17ad39e1..51201b9589 100644 --- a/.github/actions/setup-and-validate/action.yml +++ b/.github/actions/setup-and-validate/action.yml @@ -18,6 +18,9 @@ runs: run: npm ci shell: bash + - name: REUSE compliance check + uses: fsfe/reuse-action@v2 + - name: create generated dir run: mkdir ./assets/generated shell: bash diff --git a/.github/workflows/deploy_dev.yml b/.github/workflows/deploy_dev.yml new file mode 100644 index 0000000000..d92079aec8 --- /dev/null +++ b/.github/workflows/deploy_dev.yml @@ -0,0 +1,81 @@ +name: Deploy develop on dev.mapcomplete.org +on: + push: + branches: + - develop + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: "16" + cache: "npm" + cache-dependency-path: package-lock.json + + - name: install deps + run: npm ci + shell: bash + + - name: create generated dir + run: mkdir ./assets/generated + shell: bash + + - name: create dependencies + run: npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:service-worker; npm run generate:editor-layer-index + shell: bash + + - name: sync translations + run: npm run generate:translations + shell: bash + + - name: generate layeroverview + run: npm run reset:layeroverview + shell: bash + + - name: run tests + run: npm run test + shell: bash + + - name: Prepare deploy + run: npm run prepare-deploy + shell: bash + + - name: Clone deployment repo + env: + DEPLOY_KEY_PIETERVDVN: ${{ secrets.DEPLOY_KEY_PIETERVDVN }} + run: | + echo "Cloning destination repo" + git config --global user.email "pietervdvn@posteo.net" + git config --global user.name "pietervdvn" + git clone --depth 1 --single-branch --branch main "https://x-access-token:$DEPLOY_KEY_PIETERVDVN@github.com/MapComplete/mapcomplete-dev.git" + echo "Destination repo is cloned" + + - name: Sync repo + env: + DEPLOY_KEY_PIETERVDVN: ${{ secrets.DEPLOY_KEY_PIETERVDVN }} + run: | + cd mapcomplete-dev + git pull + + - name: "Copying files" + run: | + echo "Deploying" + rm -rf mapcomplete-dev/* + cp -r dist/* mapcomplete-dev/ + cd mapcomplete-dev/ + echo "dev.mapcomplete.org" > CNAME + touch .nojekyll + git add * + if git status | grep -q "Changes to be committed" + then + git commit -am "Deploying a new version" + git push + else + echo "No changes to commit" + fi + diff --git a/.github/workflows/deploy_pietervdvn.yml b/.github/workflows/deploy_pietervdvn.yml index 9552190dca..ec51d7ea70 100644 --- a/.github/workflows/deploy_pietervdvn.yml +++ b/.github/workflows/deploy_pietervdvn.yml @@ -2,7 +2,6 @@ name: Deployment on pietervdvn on: push: branches: - - develop - feature/* - theme/* - refactoring/* diff --git a/.github/workflows/theme_validation_and_deploy.yml b/.github/workflows/deploy_prod.yml similarity index 95% rename from .github/workflows/theme_validation_and_deploy.yml rename to .github/workflows/deploy_prod.yml index 35c92a86bd..1532d8e8dc 100644 --- a/.github/workflows/theme_validation_and_deploy.yml +++ b/.github/workflows/deploy_prod.yml @@ -1,4 +1,4 @@ -name: Theme Validation and deployment +name: Deploy master on mapcomplete.org on: push: branches: @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Node.js uses: actions/setup-node@v3 @@ -69,6 +69,7 @@ jobs: cp -r dist/* mapcomplete.github.io/ cd mapcomplete.github.io/ echo "mapcomplete.org" > CNAME + touch .nojekyll git add * if git status | grep -q "Changes to be committed" then diff --git a/.github/workflows/reuse-compliance-check.yml b/.github/workflows/reuse-compliance-check.yml deleted file mode 100644 index dd9c31b809..0000000000 --- a/.github/workflows/reuse-compliance-check.yml +++ /dev/null @@ -1,11 +0,0 @@ -name: REUSE Compliance Check - -on: [push, pull_request] - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: REUSE Compliance Check - uses: fsfe/reuse-action@v2 diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 2e897022ba..ebc4d9af8a 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -1,4 +1,4 @@ -name: Build and validate PR +name: Build and validate PR (but don't deploy) on: pull_request: @@ -38,53 +38,3 @@ jobs: - name: run tests run: npm run test shell: bash - - - name: Prepare deploy - run: npm run prepare-deploy - shell: bash - - name: Clone deployment repo - env: - DEPLOY_KEY_PIETERVDVN: ${{ secrets.DEPLOY_KEY_PIETERVDVN }} - run: | - echo "Cloning destination repo" - git config --global user.email "pietervdvn@posteo.net" - git config --global user.name "pietervdvn" - git clone --depth 1 --single-branch --branch master "https://x-access-token:$DEPLOY_KEY_PIETERVDVN@github.com/pietervdvn/pietervdvn.github.io.git" - echo "Destination repo is cloned" - - - name: Sync repo - env: - DEPLOY_KEY_PIETERVDVN: ${{ secrets.DEPLOY_KEY_PIETERVDVN }} - run: | - cd pietervdvn.github.io - git pull - - - name: get branch name - run: echo TARGET_BRANCH=${GITHUB_REF:11} >> $GITHUB_ENV - - - name: "Copying files" - run: | - echo "Deploying" - rm -rf pietervdvn.github.io/mc/${{ env.TARGET_BRANCH }}/* - mkdir -p pietervdvn.github.io/mc/${{ env.TARGET_BRANCH }}/ - cp -r dist/* pietervdvn.github.io/mc/${{ env.TARGET_BRANCH }}/ - cd pietervdvn.github.io/ - git add * - if git status | grep -q "Changes to be committed" - then - git commit -am "Deploying a new version of mapcomplete" - git push - else - echo "No changes to commit" - fi - env: - TARGET_BRANCH: ${{ env.TARGET_BRANCH }} - - - uses: mshick/add-pr-comment@v1 - name: Comment the PR with the review URL - if: ${{ success() && github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/master' }} - with: - message: | - [🚀 Preview Branch](https://pietervdvn.github.io/mc/${{ env.TARGET_BRANCH }}) - repo-token: ${{ secrets.GITHUB_TOKEN }} - diff --git a/.github/workflows/validate_translations.yml b/.github/workflows/validate_translations.yml deleted file mode 100644 index 6f07d343a3..0000000000 --- a/.github/workflows/validate_translations.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Deployment on pietervdvn -on: - pull_request - -jobs: - build: - runs-on: ubuntu-latest - if: ${{ github.actor == 'weblate' }} - steps: - - uses: actions/checkout@v2 - - - name: Set up Node.js - uses: actions/setup-node@v3 - with: - node-version: "16" - cache: "npm" - cache-dependency-path: package-lock.json - - - name: install deps - run: npm ci - shell: bash - - - name: create generated dir - run: mkdir ./assets/generated - shell: bash - - - name: sync translations - run: npm run generate:translations - shell: bash - - - name: generate layeroverview - run: npm run reset:layeroverview - shell: bash - - - name: run tests - run: npm run test - shell: bash diff --git a/.gitignore b/.gitignore index 41f172fe86..a9dcf16ad0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ index_*.ts .~lock.* *.doctest.ts service-worker.js +.env .vscode/* !.vscode/settings.json diff --git a/.nvmrc b/.nvmrc index 132d0eed63..06e7515969 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -nodejs 16.9.1 \ No newline at end of file +16.9.1 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index f2dca9d05f..8a636f99ee 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,10 +1,11 @@ { - "recommendations": [ - "esbenp.prettier-vscode", - "eamodio.gitlens", - "github.vscode-pull-request-github", - "svelte.svelte-vscode", - "bradlc.vscode-tailwindcss", - "editorconfig.editorconfig" - ] + "recommendations": [ + "esbenp.prettier-vscode", + "eamodio.gitlens", + "github.vscode-pull-request-github", + "svelte.svelte-vscode", + "bradlc.vscode-tailwindcss", + "editorconfig.editorconfig", + "dbaeumer.vscode-eslint" + ] } diff --git a/Docs/Layers/advertising.md b/Docs/Layers/advertising.md index d411eab76d..65cba18312 100644 --- a/Docs/Layers/advertising.md +++ b/Docs/Layers/advertising.md @@ -195,7 +195,7 @@ The question is *What kind of message is shown?* - Unselecting this answer will add - *Message from non-profit organizations* corresponds with `message=non_profit` - Unselecting this answer will add - - *To expres your opinion* corresponds with `message=opinion` + - *To express your opinion* corresponds with `message=opinion` - Unselecting this answer will add - *Religious message* corresponds with `message=religion` - Unselecting this answer will add diff --git a/Docs/Layers/bike_shop.md b/Docs/Layers/bike_shop.md index 4aa146f3dd..924e5eb3e8 100644 --- a/Docs/Layers/bike_shop.md +++ b/Docs/Layers/bike_shop.md @@ -572,6 +572,16 @@ This tagrendering has no question and is thus read-only +### delete-button + + + +This tagrendering has no question and is thus read-only + + + + + ### last_edit diff --git a/Docs/TagInfo/mapcomplete_advertising.json b/Docs/TagInfo/mapcomplete_advertising.json index 9ea6f7099d..9882b4ab68 100644 --- a/Docs/TagInfo/mapcomplete_advertising.json +++ b/Docs/TagInfo/mapcomplete_advertising.json @@ -189,7 +189,7 @@ }, { "key": "message", - "description": "Layer 'Advertise' shows message=opinion with a fixed text, namely 'To expres your opinion' and allows to pick this as a default answer (in the mapcomplete.org theme 'Advertising')", + "description": "Layer 'Advertise' shows message=opinion with a fixed text, namely 'To express your opinion' and allows to pick this as a default answer (in the mapcomplete.org theme 'Advertising')", "value": "opinion" }, { diff --git a/Docs/TagInfo/mapcomplete_personal.json b/Docs/TagInfo/mapcomplete_personal.json index 10a2f92943..7fa1790191 100644 --- a/Docs/TagInfo/mapcomplete_personal.json +++ b/Docs/TagInfo/mapcomplete_personal.json @@ -189,7 +189,7 @@ }, { "key": "message", - "description": "Layer 'Advertise' shows message=opinion with a fixed text, namely 'To expres your opinion' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", + "description": "Layer 'Advertise' shows message=opinion with a fixed text, namely 'To express your opinion' and allows to pick this as a default answer (in the mapcomplete.org theme 'Personal theme')", "value": "opinion" }, { diff --git a/Docs/Themes/elongated_coin.md b/Docs/Themes/elongated_coin.md index bfcb5b9d4b..34d69307d9 100644 --- a/Docs/Themes/elongated_coin.md +++ b/Docs/Themes/elongated_coin.md @@ -27,6 +27,7 @@ Available languages: - en - de + - es This document is autogenerated from [assets/themes/elongated_coin/elongated_coin.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/elongated_coin/elongated_coin.json) diff --git a/Docs/Themes/vending_machine.md b/Docs/Themes/vending_machine.md index 814fd49501..28f1a0cae3 100644 --- a/Docs/Themes/vending_machine.md +++ b/Docs/Themes/vending_machine.md @@ -28,6 +28,7 @@ Available languages: - en - nl - de + - fr This document is autogenerated from [assets/themes/vending_machine/vending_machine.json](https://github.com/pietervdvn/MapComplete/blob/develop/assets/themes/vending_machine/vending_machine.json) diff --git a/Docs/wikiIndex.txt b/Docs/wikiIndex.txt index 05fb889824..873ab099b5 100644 --- a/Docs/wikiIndex.txt +++ b/Docs/wikiIndex.txt @@ -247,7 +247,7 @@ {{service_item |name= [https://mapcomplete.org/elongated_coin elongated_coin] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:de|en}} +|lang= {{#language:en|en}}, {{#language:de|en}}, {{#language:es|en}} |descr= A MapComplete theme: Find penny presses to create your own elongated coins |material= {{yes|[https://mapcomplete.org/ Yes]}} |image= MapComplete_Screenshot.png @@ -499,7 +499,7 @@ {{service_item |name= [https://mapcomplete.org/vending_machine vending_machine] |region= Worldwide -|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}} +|lang= {{#language:en|en}}, {{#language:nl|en}}, {{#language:de|en}}, {{#language:fr|en}} |descr= A MapComplete theme: Find vending machines for everything |material= {{yes|[https://mapcomplete.org/ Yes]}} |image= MapComplete_Screenshot.png diff --git a/assets/layers/advertising/advertising.json b/assets/layers/advertising/advertising.json index 9ea1b3eb31..3bb77a841a 100644 --- a/assets/layers/advertising/advertising.json +++ b/assets/layers/advertising/advertising.json @@ -643,7 +643,8 @@ "fr": "Message commercial", "pt": "Mensagem comercial", "pt_BR": "Mensagem comercial", - "he": "מסר מסחרי" + "he": "מסר מסחרי", + "nl": "Commerciële boodschap" } }, { @@ -657,7 +658,8 @@ "cs": "Místní informace", "fr": "Informations locales", "pt": "Informação local", - "he": "מידע מקומי" + "he": "מידע מקומי", + "nl": "Lokale informatie" } }, { @@ -698,10 +700,10 @@ "ifnot": "message=", "then": { "ca": "Informació sobre teatres, concerts, ...", - "es": "Información sobre teatros, conciertos, ...", + "es": "Información sobre teatros, conciertos, …", "en": "Information related to theatre, concerts, …", "de": "Informationen über Theater, Konzerte, …", - "cs": "Informace týkající se divadla, koncertů, ...", + "cs": "Informace týkající se divadla, koncertů, …", "fr": "Informations liées au théâtre, à des concerts, …", "nl": "Informatie over cultuurevenementen zoals theaters, optredens, …", "pt": "Informações relacionadas com teatro, concertos, ...", @@ -786,7 +788,7 @@ "then": { "en": "A map", "ca": "un mapa", - "es": "un mapa", + "es": "Un mapa", "de": "eine Karte", "cs": "Mapa", "fr": "Une carte", @@ -1312,7 +1314,8 @@ "de": "eine Wandmalerei", "cs": "nástěnná malba", "fr": "une peinture murale", - "pt": "uma pintura de parede" + "pt": "uma pintura de parede", + "nl": "een muurschildering" }, "exampleImages": [ "./assets/themes/advertising/Capitol_wall.jpg", diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index 6f3ff86863..8d02b33d9d 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -610,23 +610,30 @@ "he": "האם יש אתר אינטרנט עם מידע נוסף על היצירה הזו?" }, "render": { - "en": "More information on this website", - "nl": "Meer informatie op deze website", - "fr": "Plus d'info sûr ce site web", - "de": "Weitere Informationen auf dieser Webseite", - "id": "Info lanjut tersedia di laman web ini", - "it": "Ulteriori informazioni su questo sito web", - "ru": "Больше информации на этом сайте", - "ja": "Webサイトに詳細情報がある", - "zh_Hant": "這個網站有更多資訊", - "nb_NO": "Mer info er å finne på denne nettsiden", - "pt": "Mais informações neste site", - "hu": "További információ ezen a weboldalon", - "pl": "Więcej informacji na tej stronie", - "es": "Más información en este sitio web", - "da": "Yderligere oplysninger på dette websted", - "cs": "Více informací na této webové stránce", - "ca": "Més informació a aquesta pàgina web" + "special": { + "type": "link", + "href": "{website}", + "text": { + "en": "More information on this website", + "nl": "Meer informatie op deze website", + "fr": "Plus d'info sûr ce site web", + "de": "Weitere Informationen auf dieser Webseite", + "id": "Info lanjut tersedia di laman web ini", + "it": "Ulteriori informazioni su questo sito web", + "ru": "Больше информации на этом сайте", + "ja": "Webサイトに詳細情報がある", + "zh_Hant": "這個網站有更多資訊", + "nb_NO": "Mer info er å finne på denne nettsiden", + "pt": "Mais informações neste site", + "hu": "További információ ezen a weboldalon", + "pl": "Więcej informacji na tej stronie", + "es": "Más información en este sitio web", + "da": "Yderligere oplysninger på dette websted", + "cs": "Více informací na této webové stránce", + "ca": "Més informació a aquesta pàgina web" + } + } + }, "freeform": { "key": "website", diff --git a/assets/layers/atm/atm.json b/assets/layers/atm/atm.json index 88ee2afbdd..f1663968cc 100644 --- a/assets/layers/atm/atm.json +++ b/assets/layers/atm/atm.json @@ -274,27 +274,8 @@ }, { "or": [ - "_country=", - "_country=at", - "_country=be", - "_country=cy", - "_country=de", - "_country=ee", - "_country=es", - "_country=fi", - "_country=fr", - "_country=gr", - "_country=hr", - "_country=ie", - "_country=it", - "_country=lt", - "_country=lu", - "_country=lv", - "_country=mt", - "_country=nl", - "_country=pt", - "_country=si", - "_country=sk" + "_currency=", + "_currency~.*EUR.*" ] } ] @@ -311,7 +292,7 @@ "mappings": [ { "if": "cash_out:notes:denominations=5 EUR", - "icon": "./assets/layers/questions/5euro.svg", + "icon": "./assets/layers/questions/denominations/eur/5euro.svg", "then": { "en": "5 euro notes can be withdrawn", "nl": "Je kunt biljetten van 5 euro afhalen", @@ -321,7 +302,7 @@ }, { "if": "cash_out:notes:denominations=10 EUR", - "icon": "./assets/layers/questions/10euro.svg", + "icon": "./assets/layers/questions/denominations/eur/10euro.svg", "then": { "en": "10 euro notes can be withdrawn", "nl": "Je kunt biljetten van 10 euro afhalen", @@ -331,7 +312,7 @@ }, { "if": "cash_out:notes:denominations=20 EUR", - "icon": "./assets/layers/questions/20euro.svg", + "icon": "./assets/layers/questions/denominations/eur/20euro.svg", "then": { "en": "20 euro notes can be withdrawn", "nl": "Je kunt biljetten van 20 euro afhalen", @@ -341,7 +322,7 @@ }, { "if": "cash_out:notes:denominations=50 EUR", - "icon": "./assets/layers/questions/50euro.svg", + "icon": "./assets/layers/questions/denominations/eur/50euro.svg", "then": { "en": "50 euro notes can be withdrawn", "nl": "Je kunt biljetten van 50 euro afhalen", @@ -351,7 +332,7 @@ }, { "if": "cash_out:notes:denominations=100 EUR", - "icon": "./assets/layers/questions/100euro.svg", + "icon": "./assets/layers/questions/denominations/eur/100euro.svg", "then": { "en": "100 euro notes can be withdrawn", "nl": "Je kunt biljetten van 100 euro afhalen", @@ -361,7 +342,7 @@ }, { "if": "cash_out:notes:denominations=200 EUR", - "icon": "./assets/layers/questions/200euro.svg", + "icon": "./assets/layers/questions/denominations/eur/200euro.svg", "then": { "en": "200 euro notes can be withdrawn", "nl": "Je kunt biljetten van 200 euro afhalen", @@ -371,7 +352,7 @@ }, { "if": "cash_out:notes:denominations=500 EUR", - "icon": "./assets/layers/questions/500euro.svg", + "icon": "./assets/layers/questions/denominations/eur/500euro.svg", "then": { "en": "500 euro notes can be withdrawn", "nl": "Je kunt biljetten van 500 euro afhalen", @@ -491,4 +472,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/assets/layers/bench/bench.json b/assets/layers/bench/bench.json index 4d394ba399..2b8395541b 100644 --- a/assets/layers/bench/bench.json +++ b/assets/layers/bench/bench.json @@ -867,7 +867,7 @@ "de": "Z.B. auf einer angebrachten Plakette, in der Rückenlehne, …", "fr": "Par exemple, sur une plaque accrochée, sur le dossier, ...", "ca": "P. ex. en una placa, al respatller, ...", - "cs": "Např. na připevněné desce, v opěradle, ...", + "cs": "Např. na připevněné desce, v opěradle, …", "pt": "Por exemplo: em placa montada, no encosto, ..." } }, diff --git a/assets/layers/bicycle_rental/bicycle_rental.json b/assets/layers/bicycle_rental/bicycle_rental.json index 5f5c74e6ad..648262cf5c 100644 --- a/assets/layers/bicycle_rental/bicycle_rental.json +++ b/assets/layers/bicycle_rental/bicycle_rental.json @@ -166,7 +166,7 @@ "es": "Este es un punto de entrega, ej. un aparcamiento reservado para colocar las bicicletas, claramente marcado como solo para el servicio de alquiler", "fr": "C'est un point de dépôt, p.ex. un emplacement de parking réservé aux vélos de location", "da": "Dette er et afleveringssted, f.eks. en reserveret parkeringsplads til cykler, som er tydeligt markeret som værende forbeholdt udlejningstjenesten", - "cs": "Jedná se o místo předání, např. vyhrazené parkoviště pro umístění jízdních kol, které je zřetelně označeno jako místo určené pouze pro půjčovnu", + "cs": "Jedná se o místo předání, např. vyhrazené parkoviště pro umístění jízdních kol, zřetelně označené jako místo určené pouze pro půjčovnu", "ca": "Aquest és un punt de baixada, p. ex. un aparcament reservat per col·locar les bicicletes marcades clarament com a només per al servei de lloguer" } } diff --git a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json index 37821672a3..bff0c569a6 100644 --- a/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json +++ b/assets/layers/bicycle_tube_vending_machine/bicycle_tube_vending_machine.json @@ -103,7 +103,7 @@ "zh_Hant": "運作狀態是 {operational_status}", "pt_BR": "O estado operacional é: {operational_status}", "pt": "O estado operacional é: {operational_status}", - "es": "El estado operacional es {operational_status}", + "es": "El estado operacional es {operational_status}", "da": "Driftsstatus er {operational_status}", "cs": "Provozní stav je {operational_status}", "ca": "L'estat operatiu és {operational_status}" diff --git a/assets/layers/bike_cafe/bike_cafe.json b/assets/layers/bike_cafe/bike_cafe.json index 55a09e1af5..521aa3ea3b 100644 --- a/assets/layers/bike_cafe/bike_cafe.json +++ b/assets/layers/bike_cafe/bike_cafe.json @@ -309,7 +309,8 @@ "pt_BR": "Quando este café de bicicleta abre?", "de": "Wann ist dieses Fahrradcafé geöffnet?", "pt": "Quando este café de bicicleta abre?", - "da": "Hvornår er denne cykelcafé åbent?" + "da": "Hvornår er denne cykelcafé åbent?", + "cs": "Kdy byla tato cyklistická kavárna otevřena?" } } } diff --git a/assets/layers/bike_cleaning/bike_cleaning.json b/assets/layers/bike_cleaning/bike_cleaning.json index 31970232e9..b7dc47440a 100644 --- a/assets/layers/bike_cleaning/bike_cleaning.json +++ b/assets/layers/bike_cleaning/bike_cleaning.json @@ -217,7 +217,7 @@ "es": "Este servicio de limpieza es de pago", "ca": "Aquest servei de neteja és de pagament", "de": "Dieser Reinigungsservice ist kostenpflichtig", - "cs": "Tato úklidová služba je placená" + "cs": "Využití úklidové služby je zpoplatněno" } } ], diff --git a/assets/layers/bike_parking/bike_parking.json b/assets/layers/bike_parking/bike_parking.json index 1bd658e187..1d6d8b0629 100644 --- a/assets/layers/bike_parking/bike_parking.json +++ b/assets/layers/bike_parking/bike_parking.json @@ -269,7 +269,7 @@ "it": "Una zona del pavimento che è marcata per il parcheggio delle bici", "de": "Ein Bereich auf dem Boden, der für das Abstellen von Fahrrädern gekennzeichnet ist", "zh_Hant": "樓層當中標示為單車停車場的區域", - "es": "Una área en el suelo que está marcada para el aparcamiento de bicicletas", + "es": "Una área en el suelo que está marcada para el aparcamiento de bicicletas", "da": "Et område på gulvet, der er markeret til cykelparkering", "cs": "Plocha na zemi označená pro parkování jízdních kol", "ca": "Una zona al terra que està senyalitzada per a l'aparcament de bicicletes" diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index f8badcbd4d..eac2303268 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -54,7 +54,8 @@ "pt": "Reparo/loja de bicicletas", "ca": "Botiga/reparació de bicicletes", "da": "Cykelværksted/butik", - "es": "Taller/tienda de bicis" + "es": "Taller/tienda de bicis", + "cs": "Oprava kol/obchod" }, "mappings": [ { @@ -165,7 +166,8 @@ "pt": "Loja de bicicletas {name}", "es": "Tienda de bicis {name}", "da": "Cykelforretning {name}", - "ca": "Botiga de bicis {name}" + "ca": "Botiga de bicis {name}", + "cs": "Prodejna kol {name}" } }, { @@ -181,7 +183,8 @@ "pt": "Loja/reparo de bicicletas {name}", "da": "Cykelværksted{name}", "es": "Taller/tienda de bicis {name}", - "ca": "Taller/botiga de bicis {name}" + "ca": "Taller/botiga de bicis {name}", + "cs": "Oprava kol/obchod {name}" } } ] diff --git a/assets/layers/bike_themed_object/bike_themed_object.json b/assets/layers/bike_themed_object/bike_themed_object.json index 6da4759b51..f4c048bde1 100644 --- a/assets/layers/bike_themed_object/bike_themed_object.json +++ b/assets/layers/bike_themed_object/bike_themed_object.json @@ -8,7 +8,8 @@ "it": "Oggetto relativo alle bici", "es": "Objeto relacionado con bicis", "da": "Cykelrelateret genstand", - "ca": "Objectes relacionats amb bicicletes" + "ca": "Objectes relacionats amb bicicletes", + "cs": "Objekt související s jízdním kolem" }, "minzoom": 13, "source": { @@ -35,7 +36,8 @@ "it": "Oggetto relativo alle bici", "es": "Objeto relacionado con bicis", "da": "Cykelrelateret objekt", - "ca": "Objecte relacionat amb bicis" + "ca": "Objecte relacionat amb bicis", + "cs": "Objekt související s jízdním kolem" }, "mappings": [ { @@ -53,7 +55,8 @@ "ru": "Велотрек", "ca": "Pista ciclable", "es": "Carril bici", - "da": "Cykelsti" + "da": "Cykelsti", + "cs": "Cyklostezka" } } ] @@ -89,6 +92,7 @@ "es": "Una capa con los objetos relacionados con bicis pero que no coinciden con ninguna otra capa", "fr": "Une couche sur le thème des vélos mais qui ne correspondent à aucune autre couche", "da": "Et lag med objekter med cykeltema, men som ikke matcher noget andet lag", - "ca": "Una capa amb els objectes relacionats amb bicis però que no coinxideixen amb cap altra capa" + "ca": "Una capa amb els objectes relacionats amb bicis però que no coinxideixen amb cap altra capa", + "cs": "Vrstva s objekty s tématikou jízdních kol, které však neodpovídají žádné jiné vrstvě" } } diff --git a/assets/layers/binocular/binocular.json b/assets/layers/binocular/binocular.json index 08fba37e6f..5246f2437d 100644 --- a/assets/layers/binocular/binocular.json +++ b/assets/layers/binocular/binocular.json @@ -9,7 +9,8 @@ "da": "Kikkert", "es": "Prismáticos", "fr": "Jumelles", - "pa_PK": "بائینوکولر" + "pa_PK": "بائینوکولر", + "cs": "Dalekohledy" }, "minzoom": 0, "title": { @@ -34,7 +35,8 @@ "da": "Kikkerter", "es": "Prismáticos", "fr": "Jumelles", - "pa_PK": "بائینوکولر" + "pa_PK": "بائینوکولر", + "cs": "Dalekohledy" }, "tagRenderings": [ "images", @@ -54,7 +56,8 @@ "da": "Gratis at bruge", "es": "De uso gratuito", "fr": "En libre service", - "ca": "Debades" + "ca": "Debades", + "cs": "Použití zdarma" } } ], @@ -80,7 +83,8 @@ "es": "¿Cuánto hay que pagar para utilizar estos prismáticos?", "da": "Hvor meget koster det at bruge denne kikkert?", "fr": "Combien l’utilisation des ces jumelles coûte-t-elle ?", - "ca": "Quant s'ha de pagar per utilitzar aquests prismàtics?" + "ca": "Quant s'ha de pagar per utilitzar aquests prismàtics?", + "cs": "Kolik se platí za používání těchto dalekohledů?" }, "id": "binocular-charge" }, @@ -123,7 +127,8 @@ "ca": "uns prismàtics", "da": "en kikkert", "es": "unos prismáticos", - "fr": "des jumelles" + "fr": "des jumelles", + "cs": "dalekohled" }, "description": { "en": "A telescope or pair of binoculars mounted on a pole, available to the public to look around. ", @@ -132,7 +137,8 @@ "fr": "Une longue-vue ou une paire de jumelles montée sur un poteau, disponible au public pour scruter les environs. ", "da": "Et teleskop eller en kikkert monteret på en stang, som offentligheden kan se sig omkring med. ", "es": "Un telescopio o unos prismáticos montados en un poste, disponible para que el público mire alrededor. ", - "ca": "Un telescopi o un parell de prismàtics muntats en un pal, a disposició del públic per mirar al seu voltant. " + "ca": "Un telescopi o un parell de prismàtics muntats en un pal, a disposició del públic per mirar al seu voltant. ", + "cs": "Jednooký teleskop nebo dalekohled umístěný na stožáru, který je k dispozici veřejnosti k prohlídce. " } } ], diff --git a/assets/layers/cafe_pub/cafe_pub.json b/assets/layers/cafe_pub/cafe_pub.json index 5e0d4a62be..50b7d834c5 100644 --- a/assets/layers/cafe_pub/cafe_pub.json +++ b/assets/layers/cafe_pub/cafe_pub.json @@ -129,7 +129,7 @@ "ca": "Bar", "de": "Kneipe", "da": "Pub", - "es": "Pub", + "es": "Bar", "fr": "Bar", "pa_PK": "پب" }, @@ -189,7 +189,7 @@ "de": "Was ist das für ein Café?", "hu": "Milyen fajta kávézó ez?", "da": "Hvilken slags cafe er dette?", - "es": "Qué tipo de cafetería es esta", + "es": "Qué tipo de cafe es este?", "fr": "Quel genre de café est-ce ?", "ca": "Quin tipus de cafeteria és aquesta?" }, diff --git a/assets/layers/charging_station/charging_station.json b/assets/layers/charging_station/charging_station.json index 43bb65a84e..c8cee649d5 100644 --- a/assets/layers/charging_station/charging_station.json +++ b/assets/layers/charging_station/charging_station.json @@ -3668,7 +3668,7 @@ "ca": "
USBper a carregar telèfons i petits dispositius electrònics
com a màxim a {socket:USB-A:current}A", "da": "
USB til opladning af telefoner og småt elektronikudstyr
udsender højst {socket:USB-A:current}A", "de": "
USB zum Aufladen von Telefonen und kleinen Elektrogeräten
liefert maximal {socket:USB-A:current} A", - "es": "
USB para carga teléfonos y dispositivos electrónicos pequeños
salida de hasta {socket:USB-A:current}A" + "es": "
USB para carga teléfonos y dispositivos electrónicos pequeños
salida de hasta {socket:USB-A:current}A" }, "freeform": { "key": "socket:USB-A:current", @@ -4431,7 +4431,7 @@ "nl": "Bij problemen, email naar {email}", "da": "I tilfælde af problemer kan du sende en e-mail til {email}", "de": "Bei Problemen senden Sie bitte eine E-Mail an {email}", - "es": "En caso de problemas, envía un correo electrónico a {email}" + "es": "En caso de problemas, envía un correo electrónico a {email}" }, "freeform": { "key": "email", diff --git a/assets/layers/climbing/climbing.json b/assets/layers/climbing/climbing.json index 5f6a4546bd..6be535d0d9 100644 --- a/assets/layers/climbing/climbing.json +++ b/assets/layers/climbing/climbing.json @@ -32,7 +32,7 @@ "club=" ] }, - "render": "{url}", + "render": "{url}", "freeform": { "key": "url", "type": "url" diff --git a/assets/layers/elevator/elevator.json b/assets/layers/elevator/elevator.json index 6893d452c0..d27464d7e5 100644 --- a/assets/layers/elevator/elevator.json +++ b/assets/layers/elevator/elevator.json @@ -140,8 +140,23 @@ } }, "induction-loop", + + {"id": "tactile_writing_available", + "question": {"en": "Has this elevator tactile writing?"}, + + "mappings":[ + {"if":"tactile_writing:braille=yes", + "then": {"en": "This elevator has tactile writing in Braille"} + }, + {"if":"tactile_writing:braille=no", + "then": {"en": "This elevator does not have tactile writing"} + } + ] + }, + { "id": "tactile_writing_language", + "condition": "tactile_writing:braille=yes", "render": { "special": { "type": "language_chooser", @@ -164,8 +179,22 @@ } } }, + {"id": "speech_output_available", + "question": {"en": "Has this elevator speech output?"}, + "questionHint": {"en": "E.g. it announces the current floor"}, + "mappings":[ + {"if":"speech_output=yes", + "then": {"en": "This elevator has speech output"} + }, + {"if":"speech_output=no", + "then": {"en": "This elevator does not have speech output"} + } + ] + }, + { "id": "speech_output", + "condition": "speech_output=yes", "render": { "special": { "type": "language_chooser", @@ -235,7 +264,8 @@ "defaultInput": "cm", "applicableUnits": [ { - "canonicalDenomination": "m", + "canonicalDenomination": "m" + , "alternativeDenomination": [ "meter" ], diff --git a/assets/layers/elongated_coin/elongated_coin.json b/assets/layers/elongated_coin/elongated_coin.json new file mode 100644 index 0000000000..0ecc82ea71 --- /dev/null +++ b/assets/layers/elongated_coin/elongated_coin.json @@ -0,0 +1,361 @@ +{ + "id": "elongated_coin", + "name": { + "en": "Penny Presses", + "de": "Münzpressen", + "es": "Prensas de centavo" + }, + "description": { + "en": "Layer showing penny presses.", + "de": "Ebene mit Münzpressen.", + "es": "Capa mostrando prensas de centavo." + }, + "source": { + "osmTags": { + "and": [ + "amenity=vending_machine", + "vending=elongated_coin" + ] + } + }, + "title": { + "render": { + "en": "Penny Press", + "de": "Münzpresse", + "es": "Prensa de centavo" + } + }, + "tagRenderings": [ + "images", + "opening_hours_24_7", + { + "id": "designs", + "question": { + "en": "How many designs are available?", + "de": "Wieviele Motive sind verfügbar?", + "es": "Cuántos diseños son disponibles?" + }, + "freeform": { + "key": "coin:design_count", + "type": "pnat", + "placeholder": { + "en": "Number of designs (e.g. 5)", + "de": "Motivanzahl (z.B. 5)", + "es": "Número de diseños (por ejemplo, 5)" + } + }, + "render": { + "en": "This penny press has {coin:design_count} designs available.", + "de": "Die Münzpresse hat {coin:design_count} Motive zur Auswahl.", + "es": "Esta prensa tiene {coin:design_count} diseños disponibles." + }, + "mappings": [ + { + "if": "coin:design_count=1", + "then": { + "en": "This penny press has one design available.", + "de": "Die Münzpresse hat ein Motiv zur Auswahl.", + "es": "Esta prensa tiene un diseño disponible." + } + }, + { + "if": "coin:design_count=2", + "then": { + "en": "This penny press has two designs available.", + "de": "Die Münzpresse hat zwei Motive zur Auswahl.", + "es": "Esta prensa tiene dos diseños disponibles." + } + }, + { + "if": "coin:design_count=3", + "then": { + "en": "This penny press has three designs available.", + "de": "Die Münzpresse hat drei Motive zur Auswahl.", + "es": "Esta prensa tiene tres diseños disponibles." + } + }, + { + "if": "coin:design_count=4", + "then": { + "en": "This penny press has four designs available.", + "de": "Die Münzpresse hat vier Motive zur Auswahl.", + "es": "Esta prensa tiene cuatro diseños disponibles." + } + } + ] + }, + { + "id": "fee", + "question": { + "en": "Does it cost money to press a penny?" + }, + "mappings": [ + { + "if": "fee=", + "then": { + "en": "It costs money to press a penny." + } + }, + { + "if": "fee=yes", + "then": { + "en": "It costs money to press a penny." + } + }, + { + "if": "fee=no", + "then": { + "en": "It is free to press a penny." + }, + "addExtraTags": [ + "payment:qr_code=", + "payment:coins=", + "payment:notes=", + "payment:debit_cards=", + "payment:credit_cards=" + ] + } + ] + }, + { + "builtin": "payment-options-split", + "override": { + "condition": { + "or": [ + "fee=yes", + "fee=" + ] + } + } + }, + { + "id": "coin", + "question": { + "en": "What coin is used for pressing?", + "de": "Welche Münze wird zum Pressen verwendet?", + "es": "Qué moneda se utiliza para presionar?" + }, + "freeform": { + "key": "coin:type", + "type": "string", + "placeholder": { + "en": "Coin type (e.g. 10cent)", + "de": "Münzenart (z.B. 10 Cent)", + "es": "Tipo de moneda (por ejemplo, 10 centavos)" + } + }, + "mappings": [ + { + "if": "coin:type=2cent", + "then": { + "en": "This penny press uses a 2 cent coin for pressing.", + "de": "Die Münzpresse benötigt eine 2 Cent Münze um zu Pressen.", + "es": "Esta prensa de centavo utiliza una moneda de 2 centavos para presionar." + }, + "hideInAnswer": { + "and": [ + "_currency!~.*EUR.*", + "_currency!~.*USD.*" + ] + } + }, + { + "if": "coin:type=5cent", + "then": { + "en": "This penny press uses a 5 cent coin for pressing.", + "de": "Die Münzpresse benötigt eine 5 Cent Münze um zu Pressen.", + "es": "Esta prensa de centavo utiliza una moneda de 5 centavos para presionar." + }, + "hideInAnswer": { + "and": [ + "_currency!~.*EUR.*", + "_currency!~.*USD.*" + ] + } + }, + { + "if": "coin:type=10cent", + "then": { + "en": "This penny press uses a 10 cent coin for pressing.", + "de": "Die Münzpresse benötigt eine 10 Cent Münze um zu Pressen.", + "es": "Esta prensa de centavo utiliza una moneda de 10 centavos para presionar." + }, + "hideInAnswer": { + "and": [ + "_currency!~.*EUR.*", + "_currency!~.*USD.*" + ] + } + }, + { + "if": "coin:type=25cent", + "then": { + "en": "This penny press uses a 25 cent coin for pressing.", + "de": "Die Münzpresse benötigt eine 25 Cent Münze um zu Pressen.", + "es": "Esta prensa de centavo utiliza una moneda de 25 centavos para presionar." + }, + "hideInAnswer": "_currency!~.*USD.*" + }, + { + "if": "coin:type=50cent", + "then": { + "en": "This penny press uses a 50 cent coin for pressing.", + "de": "Die Münzpresse benötigt eine 50 Cent Münze um zu Pressen.", + "es": "Esta prensa de centavo utiliza una moneda de 50 centavos para presionar." + }, + "hideInAnswer": "_currency!~.*USD.*" + }, + { + "if": "coin:type=10centimes", + "then": { + "en": "This penny press uses a 10 centimes coin for pressing." + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "coin:type=20centimes", + "then": { + "en": "This penny press uses a 20 centimes coin for pressing." + }, + "hideInAnswer": "_currency!~.*CHF.*" + } + ], + "render": { + "en": "This penny press uses a {coin:type} coin for pressing.", + "de": "Die Münzpresse benötigt eine {coin:type} Münze um zu Pressen.", + "es": "Esta prensa de centavo utiliza una moneda {coin:type} para presionar." + } + }, + "website", + { + "id": "charge", + "condition": { + "or": [ + "fee=yes", + "fee=" + ] + }, + "question": { + "en": "How much does it cost to press a penny?", + "de": "Wieviel kostet es eine Münze zu Pressen?", + "es": "¿Cuánto cuesta presionar un centavo?" + }, + "freeform": { + "key": "charge", + "placeholder": { + "en": "Cost (e.g. 0.50 EUR)", + "de": "Einwurf (z.B. 0,5€)", + "fr": "Coût (par ex. 0.50 EUR)", + "es": "Costo (por ejemplo, 0.50 euros)" + } + }, + "mappings": [ + { + "if": "charge=1 EUR", + "then": { + "en": "It costs 1 euro to press a penny.", + "de": "Eine Münze zu Pressen kostet 1 Euro.", + "es": "Cuesta 1 euro para presionar un centavo." + }, + "hideInAnswer": "_currency!~.*EUR.*" + }, + { + "if": "charge=2 EUR", + "then": { + "en": "It costs 2 euros to press a penny.", + "de": "Eine Münze zu Pressen kostet 2€.", + "es": "Cuesta 2 euros para presionar un centavo." + }, + "hideInAnswer": "_currency!~.*EUR.*" + }, + { + "if": "charge=2 CHF", + "then": { + "en": "It costs 2 Swiss francs to press a penny." + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "charge=1 CHF", + "then": { + "en": "It costs 1 Swiss franc to press a penny." + }, + "hideInAnswer": "_currency!~.*CHF.*" + } + ], + "render": { + "en": "It costs {charge} to press a penny.", + "de": "Es kostet {charge}€ um eine Münze zu Pressen.", + "es": "Cuesta {charge} para presionar un centavo." + } + }, + "denominations-coins", + { + "id": "indoor", + "question": { + "en": "Is the penny press indoors?", + "de": "Befindet sich die Münzpresse im Inneren?", + "es": "La prensa de centavo esta al interior?" + }, + "mappings": [ + { + "if": "indoor=yes", + "then": { + "en": "This penny press is located indoors.", + "de": "Die Münzpresse befindet sich im Inneren.", + "es": "Esta prensa está ubicada en interior." + } + }, + { + "if": "indoor=no", + "then": { + "en": "This penny press is located outdoors.", + "de": "Die Münzpresse befindet sich Draußen.", + "es": "Esta prensa está ubicada al aire libre." + } + } + ] + }, + "level", + "check_date" + ], + "mapRendering": [ + { + "icon": "circle:#FFFFFF00;./assets/themes/elongated_coin/penny.svg", + "location": [ + "point", + "centroid" + ], + "iconBadges": [ + { + "if": "opening_hours~*", + "then": "icons.isOpen" + } + ] + } + ], + "presets": [ + { + "title": { + "en": "a penny press", + "de": "Eine Münzpresse", + "es": "una prensa de centavo" + }, + "tags": [ + "amenity=vending_machine", + "vending=elongated_coin", + "payment:coins=yes" + ] + } + ], + "allowMove": { + "enableImproveAccuracy": true, + "enableRelocation": true + }, + "deletion": true, + "filter": [ + "open_now", + "accepts_debit_cards", + "accepts_credit_cards" + ] +} \ No newline at end of file diff --git a/assets/layers/filters/filters.json b/assets/layers/filters/filters.json index 1f9abce814..28a7886c54 100644 --- a/assets/layers/filters/filters.json +++ b/assets/layers/filters/filters.json @@ -10,7 +10,7 @@ { "question": { "en": "Open now", - "nl": "Nu geopened", + "nl": "Nu open", "de": "Jetzt geöffnet", "ca": "Obert ara", "es": "Abierta ahora", diff --git a/assets/layers/food/food.json b/assets/layers/food/food.json index 6030d3a353..36dcde48c5 100644 --- a/assets/layers/food/food.json +++ b/assets/layers/food/food.json @@ -1162,7 +1162,7 @@ "en": "A layer showing restaurants and fast-food amenities (with a special rendering for friteries)", "nl": "Een laag die restaurants en fast food toont (met een speciale weergave van frituren)", "de": "Eine Ebene mit Restaurants und Fast-Food-Einrichtungen (mit speziellem Rendering für Pommesbuden)", - "es": "Una capa que muestra restaurantes y locales de comida rápida (con un renderizado especial para freidurías)", + "es": "Una capa mostrando restaurantes y locales de comida rápida (con un renderizado especial para friterías)", "fr": "Un claque montrant les restaurants et les endroits de nourriture rapide (avec un rendu spécial pour les friteries)", "ca": "Una capa que mostra restaurants i locals de menjar ràpid (amb un renderitzat especial per a fregiduries)", "cs": "Vrstva zobrazující restaurace a zařízení rychlého občerstvení (se speciálním vykreslením pro fritézy)" diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index a306894a96..3bb2ab11bd 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -7,7 +7,7 @@ "it": "Bici fantasma", "fr": "Vélos fantômes", "eo": "Fantombiciklo", - "es": "Bicicleta blanca", + "es": "Bicicletas blanca", "fi": "Haamupyörä", "gl": "Bicicleta pantasma", "hu": "Emlékkerékpárok", @@ -184,14 +184,20 @@ "ca": "En quina pàgina web es pot trobar més informació sobre la bicicleta blanca o l'accident?" }, "render": { - "en": "More info available", - "nl": "Meer informatie", - "de": "Mehr Informationen", - "it": "Sono disponibili ulteriori informazioni", - "ru": "Доступна более подробная информация", - "fr": "Plus d'informations sont disponibles", - "id": "Informasi lanjut tersedia", - "ca": "Més informació disponible" + "special": { + "type": "link", + "href": "{source}", + "text": { + "en": "More info available", + "nl": "Meer informatie", + "de": "Mehr Informationen", + "it": "Sono disponibili ulteriori informazioni", + "ru": "Доступна более подробная информация", + "fr": "Plus d'informations sont disponibles", + "id": "Informasi lanjut tersedia", + "ca": "Més informació disponible" + } + } }, "freeform": { "type": "url", diff --git a/assets/layers/icons/icons.json b/assets/layers/icons/icons.json index c5633b74ac..f3b7811529 100644 --- a/assets/layers/icons/icons.json +++ b/assets/layers/icons/icons.json @@ -12,7 +12,7 @@ "labels": [ "defaults" ], - "render": "Wikipedia", + "render": "Wikipedia", "condition": { "or": [ "wikipedia~*", @@ -23,7 +23,7 @@ { "#": "ignore-image-in-then", "if": "wikipedia=", - "then": "WD" + "then": "WD" } ] }, @@ -106,7 +106,7 @@ "labels": [ "defaults" ], - "render": "website", + "render": "website", "condition": "website~*" }, { @@ -140,7 +140,7 @@ "labels": [ "defaults" ], - "render": "on osm", + "render": "on osm", "mappings": [ { "if": "id~.*/-.*", @@ -149,7 +149,7 @@ { "#": "ignore-image-in-then", "if": "_backend~*", - "then": "" + "then": "" } ], "condition": "id~(node|way|relation)/[0-9]*" diff --git a/assets/layers/maxspeed/maxspeed.json b/assets/layers/maxspeed/maxspeed.json index 1c5304cd34..65807a1d8a 100644 --- a/assets/layers/maxspeed/maxspeed.json +++ b/assets/layers/maxspeed/maxspeed.json @@ -67,7 +67,7 @@ "fr": "La vitesse maximum autorisée sur cette route est {canonical(maxspeed)}" }, "question": { - "es": "Qué velocidad tiene", + "es": "Qué es la velocidad máxima legal uno está permitido conducir en esta carretera?", "ca": "Quina és la velocitat màxima legal que es permet conduir en aquesta carretera?", "en": "What is the legal maximum speed one is allowed to drive on this road?", "de": "Wie hoch ist die zulässige Höchstgeschwindigkeit, die man auf dieser Straße fahren darf?", diff --git a/assets/layers/nature_reserve/nature_reserve.json b/assets/layers/nature_reserve/nature_reserve.json index f8c555c55f..7e9ee06697 100644 --- a/assets/layers/nature_reserve/nature_reserve.json +++ b/assets/layers/nature_reserve/nature_reserve.json @@ -360,14 +360,7 @@ "ca": "A quina adreça de correu electrònic es pot enviar amb preguntes i problemes amb aquest parc natural?" }, "render": { - "nl": "{email}", - "en": "{email}", - "ca": "{email}", - "de": "{email}", - "fr": "{email}", - "it": "{email}", - "ru": "{email}", - "id": "{email}" + "*": "{email}" }, "freeform": { "key": "email", @@ -393,7 +386,7 @@ "ca": "A quin número de telèfon es pot trucar amb preguntes i problemes amb aquest parc natural?" }, "render": { - "*": "{phone}" + "*": "{phone}" }, "freeform": { "key": "phone", diff --git a/assets/layers/parking_spaces/parking_spaces.json b/assets/layers/parking_spaces/parking_spaces.json index 7dc0f52b6a..c61f79db1b 100644 --- a/assets/layers/parking_spaces/parking_spaces.json +++ b/assets/layers/parking_spaces/parking_spaces.json @@ -153,7 +153,8 @@ "render": { "en": "This parking spaces has {capacity} spaces.", "de": "Dieser Parkplatz hat {capacity} Stellplätze.", - "nl": "Deze parkeerplek heeft {capacity} plaatsen." + "nl": "Deze parkeerplek heeft {capacity} plaatsen.", + "ca": "Aquests espais d'aparcament tenen {capacity} places." }, "mappings": [ { @@ -161,7 +162,8 @@ "then": { "en": "This parking space has 1 space.", "de": "Dieser Parkplatz hat 1 Stellplatz.", - "nl": "Deze parkeerplek heeft 1 plaats." + "nl": "Deze parkeerplek heeft 1 plaats.", + "ca": "Aquest espai d'aparcament té 1 plaça." } } ] diff --git a/assets/layers/postboxes/postboxes.json b/assets/layers/postboxes/postboxes.json index 12cf99bdbf..d662fac726 100644 --- a/assets/layers/postboxes/postboxes.json +++ b/assets/layers/postboxes/postboxes.json @@ -39,7 +39,7 @@ "id": "Layer yang memperlihatkan kotak pos.", "hu": "Postaládákat megjelenítő réteg.", "nl": "Deze laag toont brievenbussen.", - "es": "La capa que muestra buzones de correo.", + "es": "La capa que mostrando buzones de correo.", "fr": "Le calque montrant les boîtes à lettres.", "ca": "La capa que mostra bústies de correus." }, diff --git a/assets/layers/postoffices/postoffices.json b/assets/layers/postoffices/postoffices.json index 70c8b720f7..c3295a97cf 100644 --- a/assets/layers/postoffices/postoffices.json +++ b/assets/layers/postoffices/postoffices.json @@ -406,6 +406,36 @@ } } ] + }, + { + "id": "has_atm", + "question": { + "en": "Does this post office have an ATM?", + "nl": "Heeft dit postkantoor een bankautomaat?" + }, + "mappings": [ + { + "if": "atm=yes", + "then": { + "en": "This post office has an ATM", + "nl": "Dit postkantoor heeft een bankautomaat" + } + }, + { + "if": "atm=no", + "then": { + "en": "This post office does not have an ATM", + "nl": "Dit postkantoor heeft geen bankautomaaat" + } + }, + { + "if": "atm=separate", + "then": { + "en": "This post office does have an ATM, but it is mapped as a different icon", + "nl": "Dit postkantoor heeft een bankautomaat, maar deze staat apart op de kaart aangeduid" + } + } + ] } ], "presets": [ diff --git a/assets/layers/public_bookcase/public_bookcase.json b/assets/layers/public_bookcase/public_bookcase.json index 4ee12d8e04..c8a0df0170 100644 --- a/assets/layers/public_bookcase/public_bookcase.json +++ b/assets/layers/public_bookcase/public_bookcase.json @@ -438,13 +438,19 @@ }, { "render": { - "en": "More info on the website", - "nl": "Meer info op de website", - "de": "Weitere Informationen auf der Webseite", - "fr": "Plus d'infos sur le site web", - "ru": "Более подробная информация на сайте", - "it": "Maggiori informazioni sul sito web", - "hu": "További információ ezen a weboldalon" + "special": { + "type": "link", + "href": "{website}", + "text": { + "en": "More info on the website", + "nl": "Meer info op de website", + "de": "Weitere Informationen auf der Webseite", + "fr": "Plus d'infos sur le site web", + "ru": "Более подробная информация на сайте", + "it": "Maggiori informazioni sul sito web", + "hu": "További információ ezen a weboldalon" + } + } }, "question": { "en": "Is there a website with more information about this public bookcase?", diff --git a/assets/layers/questions/denominations/chf/1000chf.svg b/assets/layers/questions/denominations/chf/1000chf.svg new file mode 100644 index 0000000000..620e2802aa --- /dev/null +++ b/assets/layers/questions/denominations/chf/1000chf.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/questions/denominations/chf/1000chf.svg.license b/assets/layers/questions/denominations/chf/1000chf.svg.license new file mode 100644 index 0000000000..75299f8845 --- /dev/null +++ b/assets/layers/questions/denominations/chf/1000chf.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Robin van der Linde +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/100chf.svg b/assets/layers/questions/denominations/chf/100chf.svg new file mode 100644 index 0000000000..04148d0158 --- /dev/null +++ b/assets/layers/questions/denominations/chf/100chf.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/questions/denominations/chf/100chf.svg.license b/assets/layers/questions/denominations/chf/100chf.svg.license new file mode 100644 index 0000000000..75299f8845 --- /dev/null +++ b/assets/layers/questions/denominations/chf/100chf.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Robin van der Linde +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/10chf.svg b/assets/layers/questions/denominations/chf/10chf.svg new file mode 100644 index 0000000000..fe020f3ebf --- /dev/null +++ b/assets/layers/questions/denominations/chf/10chf.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/questions/denominations/chf/10chf.svg.license b/assets/layers/questions/denominations/chf/10chf.svg.license new file mode 100644 index 0000000000..75299f8845 --- /dev/null +++ b/assets/layers/questions/denominations/chf/10chf.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Robin van der Linde +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/10rp-2019-800px.png b/assets/layers/questions/denominations/chf/10rp-2019-800px.png new file mode 100644 index 0000000000..31b93b6387 Binary files /dev/null and b/assets/layers/questions/denominations/chf/10rp-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/10rp-2019-800px.png.license b/assets/layers/questions/denominations/chf/10rp-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/10rp-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/1fr-2019-800px.png b/assets/layers/questions/denominations/chf/1fr-2019-800px.png new file mode 100644 index 0000000000..d9d33e5bfa Binary files /dev/null and b/assets/layers/questions/denominations/chf/1fr-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/1fr-2019-800px.png.license b/assets/layers/questions/denominations/chf/1fr-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/1fr-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/200chf.svg b/assets/layers/questions/denominations/chf/200chf.svg new file mode 100644 index 0000000000..2f40ca8b34 --- /dev/null +++ b/assets/layers/questions/denominations/chf/200chf.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/questions/denominations/chf/200chf.svg.license b/assets/layers/questions/denominations/chf/200chf.svg.license new file mode 100644 index 0000000000..75299f8845 --- /dev/null +++ b/assets/layers/questions/denominations/chf/200chf.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Robin van der Linde +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/20chf.svg b/assets/layers/questions/denominations/chf/20chf.svg new file mode 100644 index 0000000000..9ee9193933 --- /dev/null +++ b/assets/layers/questions/denominations/chf/20chf.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/questions/denominations/chf/20chf.svg.license b/assets/layers/questions/denominations/chf/20chf.svg.license new file mode 100644 index 0000000000..75299f8845 --- /dev/null +++ b/assets/layers/questions/denominations/chf/20chf.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Robin van der Linde +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/20rp-2019-800px.png b/assets/layers/questions/denominations/chf/20rp-2019-800px.png new file mode 100644 index 0000000000..20317c011e Binary files /dev/null and b/assets/layers/questions/denominations/chf/20rp-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/20rp-2019-800px.png.license b/assets/layers/questions/denominations/chf/20rp-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/20rp-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/2fr-2019-800px.png b/assets/layers/questions/denominations/chf/2fr-2019-800px.png new file mode 100644 index 0000000000..728c5deba0 Binary files /dev/null and b/assets/layers/questions/denominations/chf/2fr-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/2fr-2019-800px.png.license b/assets/layers/questions/denominations/chf/2fr-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/2fr-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/50chf.svg b/assets/layers/questions/denominations/chf/50chf.svg new file mode 100644 index 0000000000..24d62bbebe --- /dev/null +++ b/assets/layers/questions/denominations/chf/50chf.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/layers/questions/denominations/chf/50chf.svg.license b/assets/layers/questions/denominations/chf/50chf.svg.license new file mode 100644 index 0000000000..75299f8845 --- /dev/null +++ b/assets/layers/questions/denominations/chf/50chf.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Robin van der Linde +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/50rp-2019-800px.png b/assets/layers/questions/denominations/chf/50rp-2019-800px.png new file mode 100644 index 0000000000..f1cd3f7aaa Binary files /dev/null and b/assets/layers/questions/denominations/chf/50rp-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/50rp-2019-800px.png.license b/assets/layers/questions/denominations/chf/50rp-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/50rp-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/5fr-2019-800px.png b/assets/layers/questions/denominations/chf/5fr-2019-800px.png new file mode 100644 index 0000000000..1b52e22734 Binary files /dev/null and b/assets/layers/questions/denominations/chf/5fr-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/5fr-2019-800px.png.license b/assets/layers/questions/denominations/chf/5fr-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/5fr-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/5rp-2019-800px.png b/assets/layers/questions/denominations/chf/5rp-2019-800px.png new file mode 100644 index 0000000000..915926a688 Binary files /dev/null and b/assets/layers/questions/denominations/chf/5rp-2019-800px.png differ diff --git a/assets/layers/questions/denominations/chf/5rp-2019-800px.png.license b/assets/layers/questions/denominations/chf/5rp-2019-800px.png.license new file mode 100644 index 0000000000..17bb1c1671 --- /dev/null +++ b/assets/layers/questions/denominations/chf/5rp-2019-800px.png.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Swissmint +SPDX-License-Identifier: CC0-1.0 \ No newline at end of file diff --git a/assets/layers/questions/denominations/chf/license_info.json b/assets/layers/questions/denominations/chf/license_info.json new file mode 100644 index 0000000000..c3119f7fb4 --- /dev/null +++ b/assets/layers/questions/denominations/chf/license_info.json @@ -0,0 +1,120 @@ +[ + { + "path": "1000chf.svg", + "license": "CC0-1.0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "100chf.svg", + "license": "CC0-1.0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "10chf.svg", + "license": "CC0-1.0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "10rp-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + }, + { + "path": "1fr-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + }, + { + "path": "200chf.svg", + "license": "CC0-1.0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "20chf.svg", + "license": "CC0-1.0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "20rp-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + }, + { + "path": "2fr-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + }, + { + "path": "50chf.svg", + "license": "CC0-1.0", + "authors": [ + "Robin van der Linde" + ], + "sources": [] + }, + { + "path": "50rp-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + }, + { + "path": "5fr-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + }, + { + "path": "5rp-2019-800px.png", + "license": "CC0-1.0", + "authors": [ + "Swissmint" + ], + "sources": [ + "https://www.swissmint.ch/swissmint/de/home/dokumentation/bildgalerie/umlaufmuenzen-bildgalerie.html" + ] + } +] \ No newline at end of file diff --git a/assets/layers/questions/100euro.svg b/assets/layers/questions/denominations/eur/100euro.svg similarity index 100% rename from assets/layers/questions/100euro.svg rename to assets/layers/questions/denominations/eur/100euro.svg diff --git a/assets/layers/questions/100euro.svg.license b/assets/layers/questions/denominations/eur/100euro.svg.license similarity index 100% rename from assets/layers/questions/100euro.svg.license rename to assets/layers/questions/denominations/eur/100euro.svg.license diff --git a/assets/layers/questions/10cent.svg b/assets/layers/questions/denominations/eur/10cent.svg similarity index 100% rename from assets/layers/questions/10cent.svg rename to assets/layers/questions/denominations/eur/10cent.svg diff --git a/assets/layers/questions/10cent.svg.license b/assets/layers/questions/denominations/eur/10cent.svg.license similarity index 100% rename from assets/layers/questions/10cent.svg.license rename to assets/layers/questions/denominations/eur/10cent.svg.license diff --git a/assets/layers/questions/10euro.svg b/assets/layers/questions/denominations/eur/10euro.svg similarity index 100% rename from assets/layers/questions/10euro.svg rename to assets/layers/questions/denominations/eur/10euro.svg diff --git a/assets/layers/questions/10euro.svg.license b/assets/layers/questions/denominations/eur/10euro.svg.license similarity index 100% rename from assets/layers/questions/10euro.svg.license rename to assets/layers/questions/denominations/eur/10euro.svg.license diff --git a/assets/layers/questions/1cent.svg b/assets/layers/questions/denominations/eur/1cent.svg similarity index 100% rename from assets/layers/questions/1cent.svg rename to assets/layers/questions/denominations/eur/1cent.svg diff --git a/assets/layers/questions/1cent.svg.license b/assets/layers/questions/denominations/eur/1cent.svg.license similarity index 100% rename from assets/layers/questions/1cent.svg.license rename to assets/layers/questions/denominations/eur/1cent.svg.license diff --git a/assets/layers/questions/1euro.svg b/assets/layers/questions/denominations/eur/1euro.svg similarity index 100% rename from assets/layers/questions/1euro.svg rename to assets/layers/questions/denominations/eur/1euro.svg diff --git a/assets/layers/questions/1euro.svg.license b/assets/layers/questions/denominations/eur/1euro.svg.license similarity index 100% rename from assets/layers/questions/1euro.svg.license rename to assets/layers/questions/denominations/eur/1euro.svg.license diff --git a/assets/layers/questions/200euro.svg b/assets/layers/questions/denominations/eur/200euro.svg similarity index 100% rename from assets/layers/questions/200euro.svg rename to assets/layers/questions/denominations/eur/200euro.svg diff --git a/assets/layers/questions/200euro.svg.license b/assets/layers/questions/denominations/eur/200euro.svg.license similarity index 100% rename from assets/layers/questions/200euro.svg.license rename to assets/layers/questions/denominations/eur/200euro.svg.license diff --git a/assets/layers/questions/20cent.svg b/assets/layers/questions/denominations/eur/20cent.svg similarity index 100% rename from assets/layers/questions/20cent.svg rename to assets/layers/questions/denominations/eur/20cent.svg diff --git a/assets/layers/questions/20cent.svg.license b/assets/layers/questions/denominations/eur/20cent.svg.license similarity index 100% rename from assets/layers/questions/20cent.svg.license rename to assets/layers/questions/denominations/eur/20cent.svg.license diff --git a/assets/layers/questions/20euro.svg b/assets/layers/questions/denominations/eur/20euro.svg similarity index 100% rename from assets/layers/questions/20euro.svg rename to assets/layers/questions/denominations/eur/20euro.svg diff --git a/assets/layers/questions/20euro.svg.license b/assets/layers/questions/denominations/eur/20euro.svg.license similarity index 100% rename from assets/layers/questions/20euro.svg.license rename to assets/layers/questions/denominations/eur/20euro.svg.license diff --git a/assets/layers/questions/2cent.svg b/assets/layers/questions/denominations/eur/2cent.svg similarity index 100% rename from assets/layers/questions/2cent.svg rename to assets/layers/questions/denominations/eur/2cent.svg diff --git a/assets/layers/questions/2cent.svg.license b/assets/layers/questions/denominations/eur/2cent.svg.license similarity index 100% rename from assets/layers/questions/2cent.svg.license rename to assets/layers/questions/denominations/eur/2cent.svg.license diff --git a/assets/layers/questions/2euro.svg b/assets/layers/questions/denominations/eur/2euro.svg similarity index 100% rename from assets/layers/questions/2euro.svg rename to assets/layers/questions/denominations/eur/2euro.svg diff --git a/assets/layers/questions/2euro.svg.license b/assets/layers/questions/denominations/eur/2euro.svg.license similarity index 100% rename from assets/layers/questions/2euro.svg.license rename to assets/layers/questions/denominations/eur/2euro.svg.license diff --git a/assets/layers/questions/500euro.svg b/assets/layers/questions/denominations/eur/500euro.svg similarity index 100% rename from assets/layers/questions/500euro.svg rename to assets/layers/questions/denominations/eur/500euro.svg diff --git a/assets/layers/questions/500euro.svg.license b/assets/layers/questions/denominations/eur/500euro.svg.license similarity index 100% rename from assets/layers/questions/500euro.svg.license rename to assets/layers/questions/denominations/eur/500euro.svg.license diff --git a/assets/layers/questions/50cent.svg b/assets/layers/questions/denominations/eur/50cent.svg similarity index 100% rename from assets/layers/questions/50cent.svg rename to assets/layers/questions/denominations/eur/50cent.svg diff --git a/assets/layers/questions/50cent.svg.license b/assets/layers/questions/denominations/eur/50cent.svg.license similarity index 100% rename from assets/layers/questions/50cent.svg.license rename to assets/layers/questions/denominations/eur/50cent.svg.license diff --git a/assets/layers/questions/50euro.svg b/assets/layers/questions/denominations/eur/50euro.svg similarity index 100% rename from assets/layers/questions/50euro.svg rename to assets/layers/questions/denominations/eur/50euro.svg diff --git a/assets/layers/questions/50euro.svg.license b/assets/layers/questions/denominations/eur/50euro.svg.license similarity index 100% rename from assets/layers/questions/50euro.svg.license rename to assets/layers/questions/denominations/eur/50euro.svg.license diff --git a/assets/layers/questions/5cent.svg b/assets/layers/questions/denominations/eur/5cent.svg similarity index 100% rename from assets/layers/questions/5cent.svg rename to assets/layers/questions/denominations/eur/5cent.svg diff --git a/assets/layers/questions/5cent.svg.license b/assets/layers/questions/denominations/eur/5cent.svg.license similarity index 100% rename from assets/layers/questions/5cent.svg.license rename to assets/layers/questions/denominations/eur/5cent.svg.license diff --git a/assets/layers/questions/5euro.svg b/assets/layers/questions/denominations/eur/5euro.svg similarity index 100% rename from assets/layers/questions/5euro.svg rename to assets/layers/questions/denominations/eur/5euro.svg diff --git a/assets/layers/questions/5euro.svg.license b/assets/layers/questions/denominations/eur/5euro.svg.license similarity index 100% rename from assets/layers/questions/5euro.svg.license rename to assets/layers/questions/denominations/eur/5euro.svg.license diff --git a/assets/layers/questions/denominations/eur/license_info.json b/assets/layers/questions/denominations/eur/license_info.json new file mode 100644 index 0000000000..2576175567 --- /dev/null +++ b/assets/layers/questions/denominations/eur/license_info.json @@ -0,0 +1,167 @@ +[ + { + "path": "100euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311346/worksheet-100-euro-coloured" + ] + }, + { + "path": "10cent.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311337/worksheet-10-cent-coloured" + ] + }, + { + "path": "10euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311343/worksheet-10-euro-coloured" + ] + }, + { + "path": "1cent.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311334/worksheet-1-cent-coloured" + ] + }, + { + "path": "1euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311340/worksheet-1-euro-coloured" + ] + }, + { + "path": "200euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311347/worksheet-200-euro-coloured" + ] + }, + { + "path": "20cent.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311338/worksheet-20-cent-coloured" + ] + }, + { + "path": "20euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311344/worksheet-20-euro-coloured" + ] + }, + { + "path": "2cent.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311335/worksheet-2-cent-coloured" + ] + }, + { + "path": "2euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311341/worksheet-2-euro-coloured" + ] + }, + { + "path": "500euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311348/worksheet-500-euro-coloured" + ] + }, + { + "path": "50cent.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311339/worksheet-50-cent-coloured" + ] + }, + { + "path": "50euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311345/worksheet-50-euro-coloured" + ] + }, + { + "path": "5cent.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311336/worksheet-5-cent-coloured" + ] + }, + { + "path": "5euro.svg", + "license": "CC0-1.0", + "authors": [ + "OpenClipart", + "frankes" + ], + "sources": [ + "https://openclipart.org/detail/311342/worksheet-5-euro-coloured" + ] + } +] \ No newline at end of file diff --git a/assets/layers/questions/license_info.json b/assets/layers/questions/license_info.json index cbc306b77a..65a360bb66 100644 --- a/assets/layers/questions/license_info.json +++ b/assets/layers/questions/license_info.json @@ -1,169 +1,4 @@ [ - { - "path": "100euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311346/worksheet-100-euro-coloured" - ] - }, - { - "path": "10cent.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311337/worksheet-10-cent-coloured" - ] - }, - { - "path": "10euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311343/worksheet-10-euro-coloured" - ] - }, - { - "path": "1cent.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311334/worksheet-1-cent-coloured" - ] - }, - { - "path": "1euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311340/worksheet-1-euro-coloured" - ] - }, - { - "path": "200euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311347/worksheet-200-euro-coloured" - ] - }, - { - "path": "20cent.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311338/worksheet-20-cent-coloured" - ] - }, - { - "path": "20euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311344/worksheet-20-euro-coloured" - ] - }, - { - "path": "2cent.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311335/worksheet-2-cent-coloured" - ] - }, - { - "path": "2euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311341/worksheet-2-euro-coloured" - ] - }, - { - "path": "500euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311348/worksheet-500-euro-coloured" - ] - }, - { - "path": "50cent.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311339/worksheet-50-cent-coloured" - ] - }, - { - "path": "50euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311345/worksheet-50-euro-coloured" - ] - }, - { - "path": "5cent.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311336/worksheet-5-cent-coloured" - ] - }, - { - "path": "5euro.svg", - "license": "CC0-1.0", - "authors": [ - "OpenClipart", - "frankes" - ], - "sources": [ - "https://openclipart.org/detail/311342/worksheet-5-euro-coloured" - ] - }, { "path": "audio_induction_loop.svg", "license": "CC-BY-4.0", diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index c3412b52d7..db5effe257 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -15,7 +15,7 @@ "id": "images", "description": "This block shows the known images which are linked with the `image`-keys, but also via `mapillary` and `wikidata` and shows the button to upload new images", "render": { - "*": "{image_carousel()}{image_upload()}" + "*": "{image_carousel()}{image_upload()}{nearby_images()}" } }, { @@ -208,7 +208,7 @@ { "id": "osmlink", "render": { - "*": "" + "*": "" }, "mappings": [ { @@ -220,7 +220,7 @@ { "id": "email", "render": { - "*": "{email}" + "*": "{email}" }, "icon": "./assets/svg/envelope.svg", "labels": [ @@ -255,7 +255,7 @@ { "if": "contact:email~*", "icon": "./assets/svg/envelope.svg", - "then": "{contact:email}", + "then": "{contact:email}", "hideInAnswer": true } ], @@ -1126,7 +1126,8 @@ }, { "or": [ - "_currency=EUR" + "_currency~.*EUR.*", + "_currency~.*CHF.*" ] } ] @@ -1146,7 +1147,7 @@ "mappings": [ { "if": "payment:coins:denominations=0.01 EUR", - "icon": "./assets/layers/questions/1cent.svg", + "icon": "./assets/layers/questions/denominations/eur/1cent.svg", "then": { "en": "1 cent coins are accepted", "de": "1-Cent-Münzen werden akzeptiert", @@ -1163,7 +1164,7 @@ }, { "if": "payment:coins:denominations=0.02 EUR", - "icon": "./assets/layers/questions/2cent.svg", + "icon": "./assets/layers/questions/denominations/eur/2cent.svg", "then": { "en": "2 cent coins are accepted", "de": "2-Cent-Münzen werden akzeptiert", @@ -1180,7 +1181,7 @@ }, { "if": "payment:coins:denominations=0.05 EUR", - "icon": "./assets/layers/questions/5cent.svg", + "icon": "./assets/layers/questions/denominations/eur/5cent.svg", "then": { "en": "5 cent coins are accepted", "de": "5-Cent-Münzen werden akzeptiert", @@ -1197,7 +1198,7 @@ }, { "if": "payment:coins:denominations=0.10 EUR", - "icon": "./assets/layers/questions/10cent.svg", + "icon": "./assets/layers/questions/denominations/eur/10cent.svg", "then": { "en": "10 cent coins are accepted", "de": "10-Cent-Münzen werden akzeptiert", @@ -1214,7 +1215,7 @@ }, { "if": "payment:coins:denominations=0.20 EUR", - "icon": "./assets/layers/questions/20cent.svg", + "icon": "./assets/layers/questions/denominations/eur/20cent.svg", "then": { "en": "20 cent coins are accepted", "de": "20-Cent-Münzen werden akzeptiert", @@ -1231,7 +1232,7 @@ }, { "if": "payment:coins:denominations=0.50 EUR", - "icon": "./assets/layers/questions/50cent.svg", + "icon": "./assets/layers/questions/denominations/eur/50cent.svg", "then": { "en": "50 cent coins are accepted", "de": "50-Cent-Münzen werden akzeptiert", @@ -1248,7 +1249,7 @@ }, { "if": "payment:coins:denominations=1 EUR", - "icon": "./assets/layers/questions/1euro.svg", + "icon": "./assets/layers/questions/denominations/eur/1euro.svg", "then": { "en": "1 euro coins are accepted", "de": "1-Euro-Münzen werden akzeptiert", @@ -1264,7 +1265,7 @@ }, { "if": "payment:coins:denominations=2 EUR", - "icon": "./assets/layers/questions/2euro.svg", + "icon": "./assets/layers/questions/denominations/eur/2euro.svg", "then": { "en": "2 euro coins are accepted", "de": "2-Euro-Münzen werden akzeptiert", @@ -1277,6 +1278,69 @@ "cs": "Jsou přijímány mince v hodnotě 2 euro" }, "hideInAnswer": "_currency!~.*EUR.*" + }, + { + "if": "payment:coins:denominations=0.05 CHF", + "icon": "./assets/layers/questions/denominations/chf/5rp-2019-800px.png", + "then": { + "en": "5 centimes coins are accepted", + "nl": "Munten van 5 rappen worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:coins:denominations=0.10 CHF", + "icon": "./assets/layers/questions/denominations/chf/10rp-2019-800px.png", + "then": { + "en": "10 centimes coins are accepted", + "nl": "Munten van 10 rappen worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:coins:denominations=0.20 CHF", + "icon": "./assets/layers/questions/denominations/chf/20rp-2019-800px.png", + "then": { + "en": "20 centimes coins are accepted", + "nl": "Munten van 20 rappen worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:coins:denominations=0.50 CHF", + "icon": "./assets/layers/questions/denominations/chf/50rp-2019-800px.png", + "then": { + "en": "½ franc coins are accepted", + "nl": "Munten van ½ frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:coins:denominations=1 CHF", + "icon": "./assets/layers/questions/denominations/chf/1fr-2019-800px.png", + "then": { + "en": "1 franc coins are accepted", + "nl": "Munten van 1 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:coins:denominations=2 CHF", + "icon": "./assets/layers/questions/denominations/chf/2fr-2019-800px.png", + "then": { + "en": "2 francs coins are accepted", + "nl": "Munten van 2 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:coins:denominations=5 CHF", + "icon": "./assets/layers/questions/denominations/chf/5fr-2019-800px.png", + "then": { + "en": "5 francs coins are accepted", + "nl": "Munten van 5 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" } ] }, @@ -1292,7 +1356,8 @@ }, { "or": [ - "_currency=EUR" + "_currency~.*EUR.*", + "_currency~.*CHF.*" ] } ] @@ -1311,7 +1376,7 @@ "mappings": [ { "if": "payment:notes:denominations=5 EUR", - "icon": "./assets/layers/questions/5euro.svg", + "icon": "./assets/layers/questions/denominations/eur/5euro.svg", "then": { "en": "5 euro notes are accepted", "nl": "Biljetten van 5 euro worden geaccepteerd", @@ -1326,7 +1391,7 @@ }, { "if": "payment:notes:denominations=10 EUR", - "icon": "./assets/layers/questions/10euro.svg", + "icon": "./assets/layers/questions/denominations/eur/10euro.svg", "then": { "en": "10 euro notes are accepted", "nl": "Biljetten van 10 euro worden geaccepteerd", @@ -1341,7 +1406,7 @@ }, { "if": "payment:notes:denominations=20 EUR", - "icon": "./assets/layers/questions/20euro.svg", + "icon": "./assets/layers/questions/denominations/eur/20euro.svg", "then": { "en": "20 euro notes are accepted", "nl": "Biljetten van 20 euro worden geaccepteerd", @@ -1356,7 +1421,7 @@ }, { "if": "payment:notes:denominations=50 EUR", - "icon": "./assets/layers/questions/50euro.svg", + "icon": "./assets/layers/questions/denominations/eur/50euro.svg", "then": { "en": "50 euro notes are accepted", "nl": "Biljetten van 50 euro worden geaccepteerd", @@ -1371,7 +1436,7 @@ }, { "if": "payment:notes:denominations=100 EUR", - "icon": "./assets/layers/questions/100euro.svg", + "icon": "./assets/layers/questions/denominations/eur/100euro.svg", "then": { "en": "100 euro notes are accepted", "nl": "Biljetten van 100 euro worden geaccepteerd", @@ -1386,7 +1451,7 @@ }, { "if": "payment:notes:denominations=200 EUR", - "icon": "./assets/layers/questions/200euro.svg", + "icon": "./assets/layers/questions/denominations/eur/200euro.svg", "then": { "en": "200 euro notes are accepted", "nl": "Biljetten van 200 euro worden geaccepteerd", @@ -1401,7 +1466,7 @@ }, { "if": "payment:notes:denominations=500 EUR", - "icon": "./assets/layers/questions/500euro.svg", + "icon": "./assets/layers/questions/denominations/eur/500euro.svg", "then": { "en": "500 euro notes are accepted", "nl": "Biljetten van 500 euro worden geaccepteerd", @@ -1413,6 +1478,60 @@ "fr": "Les billets de 500 euros sont acceptés" }, "hideInAnswer": "_currency!~.*EUR.*" + }, + { + "if": "payment:notes:denominations=10 CHF", + "icon": "./assets/layers/questions/denominations/chf/10chf.svg", + "then": { + "en": "10 francs notes are accepted", + "nl": "Biljetten van 10 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:notes:denominations=20 CHF", + "icon": "./assets/layers/questions/denominations/chf/20chf.svg", + "then": { + "en": "20 francs notes are accepted", + "nl": "Biljetten van 20 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:notes:denominations=50 CHF", + "icon": "./assets/layers/questions/denominations/chf/50chf.svg", + "then": { + "en": "50 francs notes are accepted", + "nl": "Biljetten van 50 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:notes:denominations=100 CHF", + "icon": "./assets/layers/questions/denominations/chf/100chf.svg", + "then": { + "en": "100 francs notes are accepted", + "nl": "Biljetten van 100 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:notes:denominations=200 CHF", + "icon": "./assets/layers/questions/denominations/chf/200chf.svg", + "then": { + "en": "200 francs notes are accepted", + "nl": "Biljetten van 200 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" + }, + { + "if": "payment:notes:denominations=1000 CHF", + "icon": "./assets/layers/questions/denominations/chf/1000chf.svg", + "then": { + "en": "1000 francs notes are accepted", + "nl": "Biljetten van 1000 frank worden geaccepteerd" + }, + "hideInAnswer": "_currency!~.*CHF.*" } ] }, diff --git a/assets/layers/sport_pitch/sport_pitch.json b/assets/layers/sport_pitch/sport_pitch.json index 8ad88f522d..9005470995 100644 --- a/assets/layers/sport_pitch/sport_pitch.json +++ b/assets/layers/sport_pitch/sport_pitch.json @@ -491,7 +491,7 @@ "key": "email", "type": "email" }, - "render": "{email}", + "render": "{email}", "id": "sport_pitch-email" }, { diff --git a/assets/layers/surveillance_camera/surveillance_camera.json b/assets/layers/surveillance_camera/surveillance_camera.json index ae0cb3a9cb..93527fc63a 100644 --- a/assets/layers/surveillance_camera/surveillance_camera.json +++ b/assets/layers/surveillance_camera/surveillance_camera.json @@ -215,7 +215,7 @@ "de": "Die Kamera überwacht einen öffentlichen Bereich, z. B. Straßen, Brücken, Plätze, Parks, Bahnhöfe, öffentliche Gänge oder Tunnel, …", "da": "Et offentligt område overvåges, f.eks. en gade, en bro, et torv, en park, en togstation, en offentlig korridor eller en tunnel, …", "ca": "Es vigila una àrea pública, com un carrer, un pont, una plaça, un parc, una estació de tren, un túnel públic, …", - "es": "Es un área pública, como una calle, un puente, una plaza, un parque, una estación de tren, un corredor público o túnel, ..." + "es": "Es un área pública, como una calle, un puente, una plaza, un parque, una estación de tren, un corredor público o túnel, …" } }, { diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json index dde6c27916..dc039c2953 100644 --- a/assets/layers/usersettings/usersettings.json +++ b/assets/layers/usersettings/usersettings.json @@ -406,7 +406,7 @@ "special": { "type": "multi", "key": "_translation_links", - "tagrendering": "Translate entries of {id}" + "tagrendering": "Translate entries of {id}" } } }, @@ -416,20 +416,20 @@ { "if": "_mastodon_link~*", "then": { - "en": "A link to your Mastodon-profile has been been found: {_mastodon_link}", - "de": "Es wurde ein Link zu deinem Mastodon-Profil gefunden: {_mastodon_link}", - "nl": "Een link naar je Mastodon-profiel werd gevonden: {_mastodon_link}", - "fr": "Un lien vers votre profil Mastodon a été trouvé : {_mastodon_link}", - "ca": "S'ha trobat un enllaç al vostre perfil de Mastodon: {_mastodon_link}" + "en": "A link to your Mastodon-profile has been been found: {_mastodon_link}", + "de": "Es wurde ein Link zu deinem Mastodon-Profil gefunden: {_mastodon_link}", + "nl": "Een link naar je Mastodon-profiel werd gevonden: {_mastodon_link}", + "fr": "Un lien vers votre profil Mastodon a été trouvé : {_mastodon_link}", + "ca": "S'ha trobat un enllaç al vostre perfil de Mastodon: {_mastodon_link}" }, "icon": "mastodon" }, { "if": "_mastodon_candidate~*", "then": { - "en": "We found a link to what looks to be a mastodon account, but it is unverified. Edit your profile description and place the following there: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", - "de": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. Bearbeiten Sie Ihre Profilbeschreibung und fügen Sie dort Folgendes ein: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", - "nl": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.Pas je profielbeschrijving aan en plaats er de volgende code: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" + "en": "We found a link to what looks to be a mastodon account, but it is unverified. Edit your profile description and place the following there: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", + "de": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. Bearbeiten Sie Ihre Profilbeschreibung und fügen Sie dort Folgendes ein: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>", + "nl": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.Pas je profielbeschrijving aan en plaats er de volgende code: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" }, "icon": "invalid" } diff --git a/assets/layers/vending_machine/vending_machine.json b/assets/layers/vending_machine/vending_machine.json index ace99ea956..44906a2b50 100644 --- a/assets/layers/vending_machine/vending_machine.json +++ b/assets/layers/vending_machine/vending_machine.json @@ -205,6 +205,29 @@ "de": "Blumen werden verkauft" }, "icon": "./assets/layers/id_presets/maki-florist.svg" + }, + { + "if": "vending=parking_tickets", + "then": { + "en": "Parking tickets are sold", + "nl": "Parkeerkaarten worden verkocht" + }, + "icon": "./assets/layers/parking_ticket_machine/parking_tickets.svg" + }, + { + "if": "vending=elongated_coin", + "then": { + "en": "Pressed pennies are sold" + }, + "icon": "./assets/themes/elongated_coin/penny.svg" + }, + { + "if": "vending=public_transport_tickets", + "then": { + "en": "Public transport tickets are sold", + "nl": "Openbaar vervoerkaartjes worden verkocht" + }, + "icon": "./assets/themes/stations/public_transport_tickets.svg" } ], "multiAnswer": true @@ -483,6 +506,33 @@ ] }, "then": "circle:white;./assets/layers/id_presets/maki-florist.svg" + }, + { + "if": { + "and": [ + "_vending_count>1", + "vending~.*parking_tickets.*" + ] + }, + "then": "circle:white;./assets/layers/parking_ticket_machine/parking_tickets.svg" + }, + { + "if": { + "and": [ + "_vending_count>1", + "vending~.*elongated_coin.*" + ] + }, + "then": "circle:white;./assets/themes/elongated_coin/penny.svg" + }, + { + "if": { + "and": [ + "_vending_count>1", + "vending~.*public_transport_tickets.*" + ] + }, + "then": "circle:white;./assets/themes/stations/public_transport_tickets.svg" } ] } diff --git a/assets/themes/atm/atm.json b/assets/themes/atm/atm.json index e8340a4724..86dd7a6271 100644 --- a/assets/themes/atm/atm.json +++ b/assets/themes/atm/atm.json @@ -54,7 +54,7 @@ "override": { "minzoom": 18, "filter": { - "sameAs": "bank" + "sameAs": "bank_with_atm" } } }, @@ -86,7 +86,8 @@ "de": "Diesen Geldautomaten importieren", "zh_Hant": "匯入這座 ATM", "nl": "Voeg deze ATM toe", - "fr": "Importer ce distributeur de billets" + "fr": "Importer ce distributeur de billets", + "es": "Importar este ATM" }, "icon": "./assets/svg/addSmall.svg" } @@ -97,7 +98,8 @@ "condition": "_has_closeby_feature=yes", "render": { "en": "OpenStreetMap knows about an ATM which is {_closest_osm_poi_distance} meter away. ", - "de": "OpenStreetMap kennt einen Geldautomaten, der {_closest_osm_poi_distance} Meter entfernt ist. " + "de": "OpenStreetMap kennt einen Geldautomaten, der {_closest_osm_poi_distance} Meter entfernt ist. ", + "es": "OpenStreetMap sabe sobre un ATM que es {_closest_osm_poi_distance} de distancia. " } }, { @@ -111,7 +113,8 @@ "message": { "en": "Add all the suggested tags to the closest ATM", "de": "Füge alle vorgeschlagenen Tags zum nächstgelegenen Geldautomaten hinzu", - "fr": "Ajouter tous les attributs suggérés au distributeur de billets le plus proche" + "fr": "Ajouter tous les attributs suggérés au distributeur de billets le plus proche", + "es": "Añade todas las etiquetas sugieridas al ATM más cercano" }, "image": "./assets/svg/addSmall.svg", "maproulette_task_id": "mr_taskId" @@ -126,6 +129,33 @@ "all_tags" ] } + }, + { + "builtin": "postoffices", + "override": { + "id": "post_offices_with_atm", + "minzoom": 14, + "=presets": [], + "source": { + "osmTags": { + "and+": [ + "atm=yes" + ] + } + }, + "filter": [ + "open_now" + ] + } + }, + { + "builtin": "postoffices", + "override": { + "minzoom": 18, + "filter": { + "sameAs": "post_offices_with_atm" + } + } } ] -} \ No newline at end of file +} diff --git a/assets/themes/bookcases/bookcases.json b/assets/themes/bookcases/bookcases.json index 57329e0d74..8cd1813f95 100644 --- a/assets/themes/bookcases/bookcases.json +++ b/assets/themes/bookcases/bookcases.json @@ -13,7 +13,7 @@ "nb_NO": "Kart over åpne bokhyller", "hu": "Könyvespolctérkép", "ca": "Mapa obert de prestatgeries", - "es": "Mapa abierto de estanterías", + "es": "Estanterías publicas", "pa_PK": "آزاد کتاب نقشہ", "cs": "Otevřená mapa pouličních knihoven" }, diff --git a/assets/themes/climbing/climbing.json b/assets/themes/climbing/climbing.json index 8df71bd98e..229eb50374 100644 --- a/assets/themes/climbing/climbing.json +++ b/assets/themes/climbing/climbing.json @@ -14,7 +14,7 @@ "ca": "Mapa obert d'escalada", "da": "Åbn klatrekort", "cs": "Otevřená lezecká mapa", - "es": "Mapa Abierto de Escalada" + "es": "Gimnasios de escalada, clubes y lugares" }, "description": { "nl": "Op deze kaart vind je verschillende klimgelegenheden, zoals klimzalen, bolderzalen en klimmen in de natuur", @@ -363,7 +363,8 @@ "question": { "en": "Does this shoe repair shop repair climbing shoes?", "de": "Repariert das Schuhgeschäft Kletterschuhe?", - "fr": "Est-ce que cette cordonnerie répare les chaussons d'escalade ?" + "fr": "Est-ce que cette cordonnerie répare les chaussons d'escalade ?", + "es": "¿Esta tienda de reparación de zapatos repara zapatos de escalada?" }, "mappings": [ { @@ -371,7 +372,8 @@ "then": { "en": "This shop repairs climbing shoes", "de": "Dieser Laden repariert Kletterschuhe", - "fr": "Ce commerce répare les chaussures d'escalade" + "fr": "Ce commerce répare les chaussures d'escalade", + "es": "Esta tienda repara zapatos de escalada" } }, { @@ -379,7 +381,8 @@ "then": { "en": "This shop does not repair climbing shoes", "de": "Dieser Shop repariert keine Kletterschuhe", - "fr": "Ce commerce ne répare pas les chaussures d'escalade" + "fr": "Ce commerce ne répare pas les chaussures d'escalade", + "es": "Esta tienda no repara zapatos de escalada" } } ] @@ -403,9 +406,10 @@ { "id": "repairs_climbing_shoes", "question": { - "en": "Does this shoe repair shop also repair clibming shoes?", + "en": "Does this shoe repair shop also repair climbing shoes?", "de": "Repariert dieses Schuhgeschäft auch Kletterschuhe?", - "fr": "Est-ce que cette cordonnerie répare les chaussons d'escalade ?" + "fr": "Est-ce que cette cordonnerie répare les chaussons d'escalade ?", + "es": "¿Esta tienda de reparación de zapatos también repara zapatos de escalada?" }, "mappings": [ { @@ -413,7 +417,8 @@ "then": { "en": "This shop repairs climbing shoes", "de": "Dieses Geschäft repariert Kletterschuhe", - "fr": "Ce commerce répare les chaussons d'escalade" + "fr": "Ce commerce répare les chaussons d'escalade", + "es": "Esta tienda repara zapatos de escalada" } }, { @@ -421,7 +426,8 @@ "then": { "en": "This shop does not repair climbing shoes", "de": "Dieses Geschäft repariert keine Kletterschuhe", - "fr": "Ce commerce ne répare pas les chaussons d'escalade" + "fr": "Ce commerce ne répare pas les chaussons d'escalade", + "es": "Esta tienda no repara zapatos de escalada" } } ] @@ -435,12 +441,14 @@ "title": { "en": "a shoe repair shop", "de": "Ein Schuhmacher", - "fr": "une cordonnerie" + "fr": "une cordonnerie", + "es": "una tienda de reparación de zapatos" } } ] } - } + }, + "drinking_water", "toilet" ], "credits": "Christian Neumann " } diff --git a/assets/themes/cyclofix/cyclofix.json b/assets/themes/cyclofix/cyclofix.json index 0edf472b0c..b63a0a14bd 100644 --- a/assets/themes/cyclofix/cyclofix.json +++ b/assets/themes/cyclofix/cyclofix.json @@ -12,7 +12,7 @@ "it": "Cyclofix - una mappa libera per chi va in bici", "nb_NO": "Cyclofix — et åpent kart for syklister", "hu": "Cyclofix – nyílt térkép kerékpárosoknak", - "es": "Cyclofix - un mapa abierto para ciclistas", + "es": "Cyclofix - un mapa para ciclistas", "ca": "Cyclofix - un mapa obert per a ciclistes", "da": "Cyclofix - et åbent kort for cyklister", "cs": "Cyklofix - otevřená mapa pro cyklisty" diff --git a/assets/themes/elongated_coin/elongated_coin.json b/assets/themes/elongated_coin/elongated_coin.json index ede194c931..82cce03163 100644 --- a/assets/themes/elongated_coin/elongated_coin.json +++ b/assets/themes/elongated_coin/elongated_coin.json @@ -2,255 +2,19 @@ "id": "elongated_coin", "title": { "en": "Penny Presses", - "de": "Münzpressen" + "de": "Münzpressen", + "es": "Prensa de centavo" }, "description": { "en": "Find penny presses to create your own elongated coins.", - "de": "Finde Münzpresse um deine eigenen Prägemünzen zu Pressen." + "de": "Finde Münzpresse um deine eigenen Prägemünzen zu Pressen.", + "es": "Encuentra prensas de centavo para crear tus propias monedas alargadas." }, "icon": "./assets/themes/elongated_coin/penny.svg", "layers": [ - { - "id": "elongated_coin", - "name": { - "en": "Penny Presses", - "de": "Münzpressen" - }, - "description": { - "en": "Layer showing penny presses.", - "de": "Ebene mit Münzpressen." - }, - "source": { - "osmTags": { - "and": [ - "amenity=vending_machine", - "vending=elongated_coin" - ] - } - }, - "title": { - "render": { - "en": "Penny Press", - "de": "Münzpresse" - } - }, - "tagRenderings": [ - "images", - "opening_hours_24_7", - { - "id": "designs", - "question": { - "en": "How many designs are available?", - "de": "Wieviele Motive sind verfügbar?" - }, - "freeform": { - "key": "coin:design_count", - "type": "pnat", - "placeholder": { - "en": "Number of designs (e.g. 5)", - "de": "Motivanzahl (z.B. 5)" - } - }, - "render": { - "en": "This penny press has {coin:design_count} designs available.", - "de": "Die Münzpresse hat {coin:design_count} Motive zur Auswahl." - }, - "mappings": [ - { - "if": "coin:design_count=1", - "then": { - "en": "This penny press has one design available.", - "de": "Die Münzpresse hat ein Motiv zur Auswahl." - } - }, - { - "if": "coin:design_count=2", - "then": { - "en": "This penny press has two designs available.", - "de": "Die Münzpresse hat zwei Motive zur Auswahl." - } - }, - { - "if": "coin:design_count=3", - "then": { - "en": "This penny press has three designs available.", - "de": "Die Münzpresse hat drei Motive zur Auswahl." - } - }, - { - "if": "coin:design_count=4", - "then": { - "en": "This penny press has four designs available.", - "de": "Die Münzpresse hat vier Motive zur Auswahl." - } - } - ] - }, - "payment-options-split", - { - "id": "coin", - "question": { - "en": "What coin is used for pressing?", - "de": "Welche Münze wird zum Pressen verwendet?" - }, - "freeform": { - "key": "coin:type", - "type": "string", - "placeholder": { - "en": "Coin type (e.g. 10cent)", - "de": "Münzenart (z.B. 10 Cent)" - } - }, - "mappings": [ - { - "if": "coin:type=2cent", - "then": { - "en": "This penny press uses a 2 cent coin for pressing.", - "de": "Die Münzpresse benötigt eine 2 Cent Münze um zu Pressen." - } - }, - { - "if": "coin:type=5cent", - "then": { - "en": "This penny press uses a 5 cent coin for pressing.", - "de": "Die Münzpresse benötigt eine 5 Cent Münze um zu Pressen." - } - }, - { - "if": "coin:type=10cent", - "then": { - "en": "This penny press uses a 10 cent coin for pressing.", - "de": "Die Münzpresse benötigt eine 10 Cent Münze um zu Pressen." - } - }, - { - "if": "coin:type=25cent", - "then": { - "en": "This penny press uses a 25 cent coin for pressing.", - "de": "Die Münzpresse benötigt eine 25 Cent Münze um zu Pressen." - }, - "hideInAnswer": "_currency!~.*USD.*" - }, - { - "if": "coin:type=50cent", - "then": { - "en": "This penny press uses a 50 cent coin for pressing.", - "de": "Die Münzpresse benötigt eine 50 Cent Münze um zu Pressen." - }, - "hideInAnswer": "_currency!~.*USD.*" - } - ], - "render": { - "en": "This penny press uses a {coin:type} coin for pressing.", - "de": "Die Münzpresse benötigt eine {coin:type} Münze um zu Pressen." - } - }, - "website", - { - "id": "charge", - "question": { - "en": "How much does it cost to press a penny?", - "de": "Wieviel kostet es eine Münze zu Pressen?" - }, - "freeform": { - "key": "charge", - "placeholder": { - "en": "Cost (e.g. 0.50 EUR)", - "de": "Einwurf (z.B. 0,5€)", - "fr": "Coût (par ex. 0.50 EUR)" - } - }, - "mappings": [ - { - "if": "charge=1 EUR", - "then": { - "en": "It costs 1 euro to press a penny.", - "de": "Eine Münze zu Pressen kostet 1 Euro." - }, - "hideInAnswer": "_currency!~.*EUR.*" - }, - { - "if": "charge=2 EUR", - "then": { - "en": "It costs 2 euros to press a penny.", - "de": "Eine Münze zu Pressen kostet 2€." - }, - "hideInAnswer": "_currency!~.*EUR.*" - } - ], - "render": { - "en": "It costs {charge} to press a penny.", - "de": "Es kostet {charge}€ um eine Münze zu Pressen." - } - }, - "denominations-coins", - { - "id": "indoor", - "question": { - "en": "Is the penny press indoors?", - "de": "Befindet sich die Münzpresse im Inneren?" - }, - "mappings": [ - { - "if": "indoor=yes", - "then": { - "en": "This penny press is located indoors.", - "de": "Die Münzpresse befindet sich im Inneren." - } - }, - { - "if": "indoor=no", - "then": { - "en": "This penny press is located outdoors.", - "de": "Die Münzpresse befindet sich Draußen." - } - } - ] - }, - "level", - "check_date" - ], - "mapRendering": [ - { - "icon": "circle:#FFFFFF00;./assets/themes/elongated_coin/penny.svg", - "location": [ - "point", - "centroid" - ], - "iconBadges": [ - { - "if": "opening_hours~*", - "then": "icons.isOpen" - } - ] - } - ], - "presets": [ - { - "title": { - "en": "a penny press", - "de": "Eine Münzpresse" - }, - "tags": [ - "amenity=vending_machine", - "vending=elongated_coin", - "payment:coins=yes" - ] - } - ], - "allowMove": { - "enableImproveAccuracy": true, - "enableRelocation": true - }, - "deletion": true, - "filter": [ - "open_now", - "accepts_debit_cards", - "accepts_credit_cards" - ] - } + "elongated_coin" ], "startLat": 53.0565, "startLon": 8.7492, "startZoom": 11 -} \ No newline at end of file +} diff --git a/assets/themes/etymology/etymology.json b/assets/themes/etymology/etymology.json index e949bf0603..96ac0695a6 100644 --- a/assets/themes/etymology/etymology.json +++ b/assets/themes/etymology/etymology.json @@ -13,7 +13,7 @@ "da": "Åbn oprindelseskort", "nb_NO": "Åpent etymologikart", "cs": "Otevřít etymologickou mapu", - "es": "Mapa Abierto Etimológico" + "es": "Etimología - a qué se debe el nombre de una calle?" }, "shortDescription": { "en": "What is the origin of a toponym?", diff --git a/assets/themes/ghostbikes/ghostbikes.json b/assets/themes/ghostbikes/ghostbikes.json index 676c469dda..3d80bbc440 100644 --- a/assets/themes/ghostbikes/ghostbikes.json +++ b/assets/themes/ghostbikes/ghostbikes.json @@ -9,7 +9,7 @@ "zh_Hant": "幽靈單車", "fr": "Vélo fantôme", "eo": "Fantombicikloj", - "es": "Bicicleta blanca", + "es": "Bicicletas blanca", "fi": "Haamupyörä", "gl": "Bicicleta pantasma", "hu": "Szellemkerékpárok", diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index dabbec8e1e..3bd0b3b295 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,13 +1,21 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete" + "en": "Changes made with MapComplete", + "de": "Mit MapComplete erstellte Änderungen", + "fr": "Changements faits avec MapComplete", + "nl": "Wijzigingen gemaakt met MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete" + "en": "Show changes made with MapComplete", + "de": "Mit MapComplete erstellte Änderungen anzeigen", + "nl": "Toon wijzigingen gemaakt met MapComplete" }, "description": { - "en": "This maps shows all the changes made with MapComplete" + "en": "This maps shows all the changes made with MapComplete", + "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", + "fr": "Cette carte montre tous les changements faits avec MapComplete", + "nl": "Deze kaart toont alle wijzigingen die met MapComplete gemaakt werden" }, "icon": "./assets/svg/logo.svg", "hideFromOverview": true, @@ -20,7 +28,9 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers" + "en": "Changeset centers", + "de": "Zentrum der Änderungssätze", + "nl": "Centerpunt van changeset" }, "minzoom": 0, "source": { @@ -31,41 +41,57 @@ }, "title": { "render": { - "en": "Changeset for {theme}" + "en": "Changeset for {theme}", + "de": "Änderungssatz für {theme}", + "fr": "Groupe de modifications pour {theme}" } }, "description": { - "en": "Shows all MapComplete changes" + "en": "Show all MapComplete changes", + "de": "Alle MapComplete-Änderungen anzeigen", + "nl": "Toon alle MapComplete wijzigingen" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { - "en": "Changeset {id}" + "en": "Changeset {id}", + "de": "Änderungssatz {id}", + "fr": "Groupe de modifications {id}" } }, { "id": "contributor", "question": { - "en": "What contributor did make this change?" + "en": "Which contributor made this change?", + "de": "Welcher Mitwirkende hat diese Änderung vorgenommen?", + "fr": "Quel contributeur a fait cette modification ?", + "nl": "Welke bijdrager maakte deze wijziging?" }, "freeform": { "key": "user" }, "render": { - "en": "Change made by {user}" + "en": "Change made by {user}", + "de": "Änderung gemacht von {user}", + "fr": "Modification faite par {user}", + "nl": "Wijziging gemaakt door {user}" } }, { "id": "theme-id", "question": { - "en": "What theme was used to make this change?" + "en": "What theme was used to make this change?", + "de": "Welches Thema wurde für diese Änderung verwendet?", + "fr": "Quel thème a été utilisé pour faire cette modification ?" }, "freeform": { "key": "theme" }, "render": { - "en": "Change with theme {theme}" + "en": "Change with theme {theme}", + "de": "Geändert mit Thema {theme}", + "fr": "Modifié avec le thème {theme}" } }, { @@ -74,19 +100,29 @@ "key": "locale" }, "question": { - "en": "What locale (language) was this change made in?" + "en": "What locale (language) was this change made in?", + "de": "In welcher Sprache wurde diese Änderung vorgenommen?", + "fr": "En quelle langue est-ce que ce changement a été fait ?", + "nl": "In welke locale (taal) werd deze wijziging gemaakt?" }, "render": { - "en": "User locale is {locale}" + "en": "User locale is {locale}", + "de": "Usersprache ist {locale}", + "nl": "De gebruikerstaal is {locale}" } }, { "id": "host", "render": { - "en": "Change with with {host}" + "en": "Change made with {host}", + "de": "Änderung vorgenommen mit {host}", + "fr": "Modification faite avec {host}", + "nl": "Wijziging gemaakt met {host}" }, "question": { - "en": "What host (website) was this change made with?" + "en": "What host (website) was this change made with?", + "de": "Mit welchem Host / welcher Website wurde diese Änderung gemacht?", + "nl": "Met welke host (website) werd deze wijziging gemaakt?" }, "freeform": { "key": "host" @@ -107,10 +143,14 @@ { "id": "version", "question": { - "en": "What version of MapComplete was used to make this change?" + "en": "What version of MapComplete was used to make this change?", + "de": "Mit welcher Version von MapComplete wurde diese Änderung gemacht?", + "fr": "Quelle version de MapComplete a été utilisée pour faire cette modification ?" }, "render": { - "en": "Made with {editor}" + "en": "Made with {editor}", + "de": "Erstellt mit {editor}", + "fr": "Fait avec {editor}" }, "freeform": { "key": "editor" @@ -452,7 +492,9 @@ } ], "question": { - "en": "Themename contains {search}" + "en": "Theme name contains {search}", + "de": "Themenname enthält {search}", + "nl": "Themenaam bevat {search}" } } ] @@ -468,7 +510,9 @@ } ], "question": { - "en": "Made by contributor {search}" + "en": "Made by contributor {search}", + "de": "Erstellt von {search}", + "nl": "Gemaakt door bijdrager {search}" } } ] @@ -484,7 +528,9 @@ } ], "question": { - "en": "Not made by contributor {search}" + "en": "Not made by contributor {search}", + "de": "Nicht erstellt von {search}", + "nl": "Niet gemaakt door bijdrager {search}" } } ] @@ -501,7 +547,9 @@ } ], "question": { - "en": "Made before {search}" + "en": "Made before {search}", + "de": "Erstellt vor {search}", + "nl": "Gemaakt voor {search}" } } ] @@ -518,7 +566,9 @@ } ], "question": { - "en": "Made after {search}" + "en": "Made after {search}", + "de": "Erstellt nach {search}", + "nl": "Gemaakt na {search}" } } ] @@ -534,7 +584,10 @@ } ], "question": { - "en": "User language (iso-code) {search}" + "en": "User language (iso-code) {search}", + "de": "Benutzersprache (ISO-Code) {search}", + "fr": "Langage utilisateur (code-ISO) {search}", + "nl": "De taal van de bijdrager is {search}" } } ] @@ -550,7 +603,9 @@ } ], "question": { - "en": "Made with host {search}" + "en": "Made with host {search}", + "de": "Erstellt mit Host {search}", + "nl": "Gemaakt met host {search}" } } ] @@ -561,7 +616,10 @@ { "osmTags": "add-image>0", "question": { - "en": "Changeset added at least one image" + "en": "Changeset added at least one image", + "de": "Changeset fügte mindestens ein Bild hinzu", + "fr": "Le groupe de modifications a ajouté au moins une image", + "nl": "Changeset bevat minstens één afbeelding" } } ] @@ -576,7 +634,9 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here" + "en": "More statistics can be found here", + "de": "Mehr Statistiken gibt es hier", + "fr": "D'autres statistiques sont disponibles ici" } }, { @@ -606,4 +666,4 @@ } } ] -} \ No newline at end of file +} diff --git a/assets/themes/vending_machine/vending_machine.json b/assets/themes/vending_machine/vending_machine.json index 177b7f3146..ccf99528af 100644 --- a/assets/themes/vending_machine/vending_machine.json +++ b/assets/themes/vending_machine/vending_machine.json @@ -17,6 +17,46 @@ "startLon": 6.56511, "startZoom": 19, "layers": [ - "vending_machine" + "vending_machine", + { + "builtin": "vending_machine", + "override": { + "id": "all_vending_machine", + "name": null, + "filter": { + "sameAs": "vending_machine" + }, + "minzoom": 18, + "source": { + "osmTags": { + "and": [ + "amenity=vending_machine", + "vending!~(parking_tickets|elongated_coin|public_transport_tickets)" + ] + } + } + } + }, + { + "builtin": "parking_ticket_machine", + "override": { + "name": null, + "minzoom": 18 + } + }, + { + "builtin": "elongated_coin", + "override": { + "name": null, + "minzoom": 18 + } + }, + { + "builtin": "ticket_machine", + "override": { + "name": null, + "minzoom": 18 + } + } ] } \ No newline at end of file diff --git a/land.html b/land.html index cd155ea56a..a9fd46b34d 100644 --- a/land.html +++ b/land.html @@ -2,9 +2,6 @@ MapComplete Auth - + diff --git a/langs/ca.json b/langs/ca.json index 04303f2da1..0652a2cb8f 100644 --- a/langs/ca.json +++ b/langs/ca.json @@ -122,6 +122,7 @@ "isApplied": "S'aplicaran els canvis" }, "attribution": { + "attributionBackgroundLayerWithCopyright": "La capa de fons actual és {name}: {copyright}", "attributionContent": "

Totes les dades provenen d'OpenStreetMap, i es poden reutilitzar lliurement sota la Llicència Oberta de Base de Dades (ODbL).

", "attributionTitle": "Avís d’atribució", "codeContributionsBy": "MapComplete ha estat fet per {contributors} i {hiddenCount} més contribuïdors", @@ -357,20 +358,6 @@ "doDelete": "Esborrar imatge", "dontDelete": "Cancel·lar", "isDeleted": "Esborrada", - "nearbyPictures": { - "allFiltered": "No hi ha cap imatge que coincideixi amb el vostre filtre", - "browseNearby": "Cerca imatges properes…", - "confirm": "La imatge seleccionada mostra {title()}", - "hasMatchingPicture": "Una imatge coincideix amb l'objecte? Seleccioneu-lo a continuació", - "loadMore": "Carrega més imatges", - "loading": "Carregant imatges properes…", - "noImageSelected": "Seleccioneu una imatge per enllaçar-la a l'objecte", - "nothingFound": "No s'han trobat imatges properes…", - "onlyTowards": "Mostra només les imatges fetes cap a aquest objecte", - "removeFilters": "Feu clic aquí per eliminar els filtres", - "title": "Imatges properes", - "withinRadius": "Mostra només les imatges que s'han fet a {radius} metres d'aquest objecte" - }, "pleaseLogin": "Entrar per pujar una foto", "respectPrivacy": "Respecta la privacitat. No fotografiïs gent o matrícules. No facis servir imatges de Google Maps, Google Streetview o altres fonts amb copyright.", "toBig": "La teva imatge és massa gran ara que medeix {actual_size}. Usa imatges de com a molt {max_size}", diff --git a/langs/cs.json b/langs/cs.json index 4ae073795d..e5229e6b9f 100644 --- a/langs/cs.json +++ b/langs/cs.json @@ -357,20 +357,6 @@ "doDelete": "Odebrat obrázek", "dontDelete": "Zrušit", "isDeleted": "Smazáno", - "nearbyPictures": { - "allFiltered": "Filtru neodpovídají žádné obrázky", - "browseNearby": "Procházet obrázky z okolí…", - "confirm": "Na vybraném obrázku je vidět {title()}", - "hasMatchingPicture": "Odpovídá obrázek objektu? Vyberte jej níže", - "loadMore": "Načíst další obrázky", - "loading": "Načítání obrázků z okolí…", - "noImageSelected": "Výběrem obrázku jej propojíte s objektem", - "nothingFound": "V okolí nebyly nalezeny žádné obrázky…", - "onlyTowards": "Zobrazit pouze fotky pořízené směrem k tomuto objektu", - "removeFilters": "Kliknutím sem odstraníte filtry", - "title": "Obrázky v okolí", - "withinRadius": "Zobrazit pouze fotky pořízené v okruhu {radius} metrů kolem tohoto objektu" - }, "pleaseLogin": "Pro přidání fotky se prosím přihlaste", "respectPrivacy": "Nefotografujte osoby ani poznávací značky. Nevkládejte Mapy Google, Google Streetview ani jiné zdroje chráněné autorskými právy.", "toBig": "Váš obrázek je příliš velký, protože má velikost {actual_size}. Používejte prosím obrázky o maximální velikosti {max_size}", diff --git a/langs/da.json b/langs/da.json index 9b5bf0f244..bca653a48b 100644 --- a/langs/da.json +++ b/langs/da.json @@ -257,20 +257,6 @@ "doDelete": "Fjern billede", "dontDelete": "Afbryd", "isDeleted": "Slettet", - "nearbyPictures": { - "allFiltered": "Ingen billeder matchede dit filter", - "browseNearby": "Gennemse billeder i nærheden…", - "confirm": "Det valgte billede viser {title()}", - "hasMatchingPicture": "Passer et billede til objektet? Vælg det nedenfor", - "loadMore": "Indlæs flere billeder", - "loading": "Indlæser billeder i nærheden…", - "noImageSelected": "Vælg et billede for at linke det til objektet", - "nothingFound": "Ingen billeder i nærheden fundet…", - "onlyTowards": "Vis kun billeder, der er taget mod dette objekt", - "removeFilters": "Klik her for at fjerne filtrene", - "title": "Nærliggende billeder", - "withinRadius": "Vis kun billeder, der er taget inden for {radius} meter fra dette objekt" - }, "pleaseLogin": "Log venligst ind for at tilføje et billede", "respectPrivacy": "Tag ikke billeder af mennesker eller nummerplader. Upload ikke Google Maps, Google Streetview, eller fra andre ophavsresbeskyttede kilder.", "toBig": "Dit billede er for stort da det er {actual_size}. Brug venligst billeder, der er højst {max_size}", diff --git a/langs/de.json b/langs/de.json index e537e4ff88..f3d74c6c39 100644 --- a/langs/de.json +++ b/langs/de.json @@ -95,7 +95,7 @@ "404": "Diese Seite existiert nicht", "about": "OpenStreetMap für ein bestimmtes Thema einfach bearbeiten und hinzufügen", "aboutMapComplete": { - "intro": "Nutze MapComplete, um Daten zu einem bestimmten Thema auf OpenStreetMap einzutragen. Beantworte Frage, und in wenigen Minuten sind die Beiträge überall verfügbar. In den meisten Themen kannst Du Bilder hinzufügen oder sogar eine Bewertung hinterlassen. Die Themen-Ersteller*innen definieren Objekte, Fragen und Begriffe für das jeweilige Thema." + "intro": "Nutze MapComplete, um Daten zu einem bestimmten Thema auf OpenStreetMap einzutragen. Beantworte Fragen, und in wenigen Minuten sind deine Beiträge überall verfügbar. In den meisten Themen kannst Du Bilder hinzufügen oder sogar eine Bewertung hinterlassen. Die Themen-Ersteller*innen definieren Objekte, Fragen und Begriffe für das jeweilige Thema." }, "add": { "addNew": "{category} hinzufügen", @@ -404,20 +404,6 @@ "doDelete": "Bild entfernen", "dontDelete": "Abbrechen", "isDeleted": "Gelöscht", - "nearbyPictures": { - "allFiltered": "Keine Bilder passen zu Ihrem Filter", - "browseNearby": "Bilder in der Nähe suchen…", - "confirm": "Das ausgewählte Bild zeigt {title()}", - "hasMatchingPicture": "Passt ein Bild zum Objekt? Wählen Sie es unten aus", - "loadMore": "Weitere Bilder laden", - "loading": "Bilder in der Nähe laden…", - "noImageSelected": "Wählen Sie ein Bild aus, um es mit dem Objekt zu verknüpfen", - "nothingFound": "Keine Bilder in der Nähe gefunden…", - "onlyTowards": "Nur Bilder anzeigen, die in Richtung dieses Objekts aufgenommen wurden", - "removeFilters": "Hier klicken, um die Filter zu entfernen", - "title": "Bilder in der Nähe", - "withinRadius": "Nur Bilder anzeigen, die im Umkreis von {radius} Metern um dieses Objekt aufgenommen wurden" - }, "pleaseLogin": "Bitte anmelden, um ein Bild hinzuzufügen", "respectPrivacy": "Bitte respektieren Sie die Privatsphäre. Fotografieren Sie weder Personen noch Nummernschilder. Benutzen Sie keine urheberrechtlich geschützten Quellen wie z.B. Google Maps oder Google Streetview.", "toBig": "Ihr Bild ist mit {actual_size} zu groß. Die maximale Bildgröße ist {max_size}", @@ -575,7 +561,7 @@ "editDescription": "Eigene Profilbeschreibung bearbeiten", "gotoInbox": "Deinen Posteingang öffnen", "gotoSettings": "Einstellungen auf OpenStreetMap.org öffnen", - "noDescription": "Sie haben noch keine Profilbeschreibung", + "noDescription": "Noch keine Profilbeschreibung vorhanden", "noDescriptionCallToAction": "Profilbeschreibung hinzufügen", "notLoggedIn": "Du hast Dich abgemeldet" }, diff --git a/langs/en.json b/langs/en.json index 7544255c60..cc9974402e 100644 --- a/langs/en.json +++ b/langs/en.json @@ -405,19 +405,10 @@ "doDelete": "Remove image", "dontDelete": "Cancel", "isDeleted": "Deleted", - "nearbyPictures": { - "allFiltered": "No images matched your filter", - "browseNearby": "Browse nearby images…", - "confirm": "The selected image shows {title()}", - "hasMatchingPicture": "Does a picture match the object? Select it below", - "loadMore": "Load more images", - "loading": "Loading nearby images…", - "noImageSelected": "Select an image to link it to the object", - "nothingFound": "No nearby images found…", - "onlyTowards": "Only show pictures which are taken towards this object", - "removeFilters": "Click here to remove the filters", - "title": "Nearby pictures", - "withinRadius": "Only show pictures which are taken within {radius} meter of this object" + "nearby": { + "link": "This picture shows the object", + "seeNearby": "Browse and link nearby pictures", + "title": "Nearby streetview imagery" }, "pleaseLogin": "Please log in to add a picture", "respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.", diff --git a/langs/eo.json b/langs/eo.json index 67d6e9b087..8b60698969 100644 --- a/langs/eo.json +++ b/langs/eo.json @@ -94,7 +94,6 @@ "image": { "addPicture": "Aldoni bildon", "dontDelete": "Nuligi", - "nearbyPictures": {}, "pleaseLogin": "Bonvolu saluti por aldoni bildon", "uploadingMultiple": "Alŝutante {count} bildojn…", "uploadingPicture": "Alŝutante vian bildon…" diff --git a/langs/es.json b/langs/es.json index c083b94a84..815880080d 100644 --- a/langs/es.json +++ b/langs/es.json @@ -1,14 +1,20 @@ { + "advanced": { + "title": "Funciones avanzadas" + }, "centerMessage": { + "allFilteredAway": "Ningun elemento a la vista cumple todos los filtros", "loadingData": "Cargando datos…", + "noData": "No hay elementos pertinentes en la vista actual", "ready": "Hecho!", "retrying": "La carga de datos ha fallado. Volviéndolo a probar en {count} segundos…", "zoomIn": "Amplía para ver o editar los datos" }, "communityIndex": { "available": "Esta comunidad habla {native}", - "intro": "Ponte en contacto con otras personas para conocerlas, aprender de ellas, ...", - "notAvailable": "Esta comunidad no habla {native}" + "intro": "Ponte en contacto con otras personas para conocerlas, aprender de ellas, …", + "notAvailable": "Esta comunidad no habla {native}", + "title": "Pónte en contacto con otros" }, "delete": { "cancel": "Cancelar", @@ -44,34 +50,70 @@ "panelIntro": "

Tu tema personal

Activa tus capas favoritas de todas los temas oficiales", "reload": "Recargar datos" }, + "flyer": { + "aerial": "Este mapa utiliza un fondo diferente, concretamente imagines aéreas por Agentschap Informatie Vlaanderen", + "callToAction": "Pruebalo en mapcomplete.org", + "cyclofix": "Bombas de bicicleta, estaciónes de reparación, agua potable y tiendas de ciclo estan en CycloFix", + "description": "Un folleto A4-landscape para promover a MapComplete", + "editing": { + "ex": "A continuación se muestra un ejemplo simplificado de lo que parece para una reserva natural.", + "intro": "El usuario es recibido por un mapa con elementos. Al seleccionar uno, se muestra la información sobre ese elemento.", + "title": "Cómo se ve la interfaz?" + }, + "examples": "Hay muchos mapas temáticos disponibles de los cuales algunos se muestran aquí.\n\nHay muchos más mapas temáticos en línea: sobre salud, navegación interior, accesibilidad para sillas de ruedas, instalaciones de desecho, librerías públicas, cruces peatonales con arco iris,... Descubre los todos en mapcomplete.org", + "fakeui": { + "add_images": "Añade imágenes con unos pocos clics", + "attributes": "Muestra atributos de una manera amistosa", + "edit": "Información incorrecta o anticuada? El botón de edición está justo ahí.", + "question": "Si un atributo aún no se conoce, MapComplete muestra una pregunta", + "see_images": "Muestra imágenes de colaboradores anteriores, Wikipedia, Mapillary, …" + }, + "frontParagraph": "MapComplete es una aplicación web fácil de usar para recopilar geodata en OpenStreetMap, lo que permite recopilar y gestionar datos relevantes de forma abierta, con recursos de multitud y reutilizable.\n\nSe pueden añadir nuevas categorías y atributos a petición.", + "lines_too": "También se muestran líneas y polígonos. Los atributos y imágenes también se pueden añadir y actualizar en esos objetos.", + "mapcomplete": { + "customize": "MapComplete se puede adaptar a sus necesidades, con nuevas capas de mapa, nuevas funcionalidades o estilo con los colores y fuentes de sus organizaciones.\nTambién tenemos experiencia con iniciar campañas para geodata de fuente collectiva.\nContacta pietervdvn@posteo.net para una cuota.", + "intro": "MapComplete es un sitio web que tiene {mapCount} mapas interactivos. Cada mapa permite añadir o actualizar información. Tiene muchas características:", + "li0": "Muestra dónde están PDI", + "li1": "Añade nuevos puntos y actualiza información sobre los existentes", + "li2": "Añade información de contacto y horarios de apertura fácilmente", + "li3": "Se puede colocar en otros sitios web como iFrame", + "li4": "Incrustado dentro del ecosistema OpenStreetMap, que tiene muchas herramientas disponibles", + "li5": "Funcionalidad para importar conjuntos de datos existentes", + "li6": "Muchas características avanzadas, como detección de árboles y métodos avanzados de entrada", + "li7": "Copiado software libre (licenciado GPL) y gratis para usar", + "title": "Qué es MapComplete?" + }, + "onwheels": "Mapas interiores para usuarios de silla de ruedas también están disponibles.", + "osm": "OpenStreetMapa es un mapa en línea que puede ser editado y reutilizado por cualquiera para cualquier propósito mientras se da la atribución y los datos se mantienen abiertos.\n\nEs la base de datos geoespacial más grande del mundo y es reutilizada por miles de aplicaciones y sitios web." + }, "general": { "about": "Edita OpenStreetMap fácilmente y añade puntos sobre un tema concreto", "add": { "addNew": "Añadir {category}", "backToSelect": "Selecciones una categoría distinta", - "confirmButton": "Añadir una {category}
Tu contribución es visible para todos
", + "confirmButton": "Añade una {category}
Tu adición es visible para todos
", "disableFilters": "Desactivar todos los filtros", "disableFiltersExplanation": "Algunas características pueden estar ocultas por un filtro", - "hasBeenImported": "Este punto ya ha sido importado", + "hasBeenImported": "Este elemento ya ha sido importado", "import": { "hasBeenImported": "Este objeto ya ha sido importado", "howToTest": "Para probar, añade test=true o backend=osm-test a la URL. El conjunto de cambios se imprimirá en la consola. Por favor abre un PR para oficializar este tema o activar el botón \"importar\".", "importTags": "El elemento recibirá {tags}", "officialThemesOnly": "El botón de importación está desactivado para los temas no oficiales para evitar accidentes", - "wrongType": "Este elemento no es un punto o una vía y no puede ser importado", + "wrongType": "Este elemento no es un nodo o una vía y no puede ser importado", "zoomInMore": "Ampliar más para importar este elemento" }, "importTags": "El elemento recibirá {tags}", "intro": "Has marcado un lugar del que no conocemos los datos.
", - "layerNotEnabled": "La capa {layer} no está habilitada. Hazlo para poder añadir un punto en esta capa", + "layerNotEnabled": "La capa {layer} no está habilitada. Activa esta capa para poder añadir un elemento", "openLayerControl": "Abrir el control de capas", - "pleaseLogin": "Por favor inicia sesión para añadir un nuevo punto", + "pleaseLogin": "Por favor inicia sesión para añadir un nuevo elemento", "presetInfo": "El nuevo POI tendrá {tags}", "stillLoading": "Los datos se siguen cargando. Espera un poco antes de añadir una nueva función.", - "title": "Quieres añadir un punto?", + "title": "Quieres añadir un elemento?", "warnVisibleForEveryone": "Su adición será visible para todos", - "wrongType": "Este elemento no es un punto o una vía y no puede ser importado", - "zoomInFurther": "Acerca para añadir un punto.", + "wrongType": "Este elemento no es un nodo o una vía y no puede ser importado", + "zoomInFurther": "Acercate mas para añadir un elemento.", "zoomInMore": "Aumente el zoom para importar este elemento" }, "apply_button": { @@ -102,7 +144,7 @@ }, "back": "Atrás", "backToIndex": "Volver a la vista general con todos los mapas temáticos", - "backgroundMap": "Mapa de fondo", + "backgroundMap": "Seleccione una capa de fondo", "cancel": "Cancelar", "confirm": "Confirmar", "customThemeIntro": "

Temas personalizados

Estos son los temas generados por los usuarios que han sido visitados previamente.", @@ -110,7 +152,7 @@ "downloadAsPdf": "Descargar un PDF del mapa actual", "downloadAsPdfHelper": "Ideal para imprimir el mapa actual", "downloadAsSvg": "Descargar un SVG del mapa actual", - "downloadAsSvgHelper": "Compatible con Inkscape o Adobe Illustrator; necesitará más procesado·· ", + "downloadAsSvgHelper": "Compatible con Inkscape o Adobe Illustrator; necesitará más procesado", "downloadCSV": "Descargar los datos visibles como CSV", "downloadCSVHelper": "Compatible con LibreOffice Calc, Excel, …", "downloadFeatureAsGeojson": "Descargar como un archivo GeoJSON", @@ -121,15 +163,15 @@ "downloadGpxHelper": "Un archivo GPX puede ser utilizado con la mayor parte de dispositivos y aplicaciones de navegación", "exporting": "Exportando…", "includeMetaData": "Incluir metadatos (último editor, valores calculados, ...)", - "licenseInfo": "

Aviso de derechos

Los datos provistos están disponibles bajo ODbL. Reutilizarlos es gratis para cualquier propósito, pero
  • la atribución © contribuidores de OpenStreetMap se requiere
  • Cualquier cambio debe de utilizar la licencia
Por favor leer todo el aviso de derechos para detalles.", + "licenseInfo": "

Aviso de derechos

Los datos provistos están disponibles bajo ODbL. Reutilizarlos es gratis para cualquier propósito, pero
  • la atribución © contribuidores de OpenStreetMap se requiere mostrar
  • Cualquier cambio debe de utilizar la misma licencia
Por favor lea todo el aviso de derechos para mas detalles.", "noDataLoaded": "Aún no se han cargado ningunos daos. La descarga estará disponible proximamente", - "title": "Descargar los datos visibles", + "title": "Descarga", "uploadGpx": "Sube tu traza a OpenStreetMap" }, "error": "Algo fue mal", "example": "Ejemplo", "examples": "Ejemplos", - "fewChangesBefore": "Contesta unas cuantas preguntas sobre puntos existentes antes de añadir nuevos.", + "fewChangesBefore": "Por favor, responda algunas preguntas de elementos existentes antes de añadir un nuevo elemento.", "getStartedLogin": "Entra en OpenStreetMap para empezar", "getStartedNewAccount": " o crea una nueva cuenta", "goToInbox": "Abrir mensajes", @@ -143,7 +185,7 @@ "loading": "Cargando…", "loadingTheme": "Cargando {theme}…", "loginFailed": "El inicio de sesión en OpenStreetMap falló", - "loginOnlyNeededToEdit": "Si quieres editar el mapa", + "loginOnlyNeededToEdit": "si quieres hacer cambios", "loginToStart": "Entra para contestar esta pregunta", "loginWithOpenStreetMap": "Acceder con OpenStreetMap", "logout": "Cerrar la sesión", @@ -173,13 +215,13 @@ "loadingCountry": "Determinando país…", "not_all_rules_parsed": "El horario de esta tienda es complejo. Las normas siguientes serán ignoradas en la entrada:", "openTill": "hasta", - "open_24_7": "Abierto las 24 horas del día", + "open_24_7": "Abierto todo el día", "open_during_ph": "Durante fiestas este servicio está", "opensAt": "desde", "ph_closed": "cerrado", "ph_not_known": " ", "ph_open": "abierto", - "ph_open_as_usual": "abierto como siempre" + "ph_open_as_usual": "abierto, como siempre" }, "osmLinkTooltip": "Mira este objeto en OpenStreetMap para ver historial y otras opciones de edición", "pdf": { @@ -192,12 +234,12 @@ "questions": { "emailIs": "La dirección de correo de {category} es {email}", "emailOf": "¿Qué dirección de correu tiene {category}?", - "phoneNumberIs": "El número de teléfono de {category} es {phone}", + "phoneNumberIs": "El número de teléfono de esta {category} es {phone}", "phoneNumberOf": "Qué teléfono tiene {category}?", "websiteIs": "Página web: {website}", "websiteOf": "Cual es la página web de {category}?" }, - "readYourMessages": "Lee todos tus mensajes de OpenStreetMap antes de añadir nuevos puntos.", + "readYourMessages": "Lee todos tus mensajes de OpenStreetMap antes de añadir nuevos elementos.", "removeLocationHistory": "Eliminar el historial de ubicaciones", "returnToTheMap": "Volver al mapa", "save": "Guardar", @@ -268,20 +310,6 @@ "doDelete": "Borrar imagen", "dontDelete": "Cancelar", "isDeleted": "Borrada", - "nearbyPictures": { - "allFiltered": "Ninguna imagen coincide con tu filtro", - "browseNearby": "Buscar imágenes cercanas…", - "confirm": "La imagen seleccionada muestra {title()}", - "hasMatchingPicture": "¿Esta imagen coincide con el objeto? Selecciónalo debajo", - "loadMore": "Cargar más imágenes", - "loading": "Cargando imágenes cercanas…", - "noImageSelected": "Selecciona una imagen para enlazarla al objeto", - "nothingFound": "No se encontraron imágenes cercanas…", - "onlyTowards": "Solo mostrar imágenes que fueron sacadas hacia este objeto", - "removeFilters": "Haz clic aquí para eliminar los filtros", - "title": "Imágenes cercanas", - "withinRadius": "Solo mostrar imágenes que fueran sacadas dentro de un radio de {radius} metros para este objeto" - }, "pleaseLogin": "Acceda para cargar una imagen", "respectPrivacy": "No fotografíe personas ni matrículas. No cargue datos de Google Maps, Google StreetView u otras fuentes protegidas por derechos de autor.", "toBig": "Tu imagen es demasiado grande, ya que pesa {actual_size}. Por favor utiliza imágenes de como máximo {max_size}", @@ -370,7 +398,7 @@ "loadingWikidata": "Cargando información sobre {species}…" }, "privacy": { - "editing": "Cuando efectúas un cambio al mapa, este cambio se grabas en OpenStreetMap y está disponible públicamente a cualquiera. Un conjunto de cambios hecho con MapComplete incluye los siguientes datos:
  • Los cambios que has hecho
  • Tu nombre de usuario
  • Cuándo se efectuó el cambio
  • El tema que utilizaste mientras que hacías el cambio
  • El idioma de la interfaz de usuario
  • Una indicación de como de cerca estabas a los objetos cambiados. Otros mapeadores pueden utilizar esta información para determina si un cambio se hizo basándose en un sondeo o en una investigación remota
Por favor ve a ", + "editing": "Cuando efectúas un cambio al mapa, este cambio se graba en OpenStreetMap y está disponible públicamente a cualquiera. Un conjunto de cambios hecho con MapComplete incluye los siguientes datos:
  • Los cambios que has hecho
  • Tu nombre de usuario
  • Cuándo se efectuó el cambio
  • El tema que utilizaste mientras que hacías el cambio
  • El idioma de la interfaz de usuario
  • Una indicación de como de cerca estabas a los objetos cambiados. Otros mapeadores pueden utilizar esta información para determina si un cambio se hizo basándose en un sondeo o en una investigación remota
Por favor consulte la política de privacidad en OpenStreetMap.org para información detallada. Te queremos recordar que puedes utilizar un nombre ficticio al inscribirte.", "editingTitle": "Cuando se hagan cambios", "geodata": "Cuando MapComplete consigue tu geolocalización, tu geolocalización y las localizaciones previamente visitadas se mantienen en tu dispositivo. Tus datos de localización nunca se envían automáticamente a ningún otro sitio - a menos que alguna funcionalidad mencione otra cosa claramente.", "geodataTitle": "Tu geoubicación", @@ -386,7 +414,7 @@ "reviews": { "affiliated_reviewer_warning": "(Revisión afiliada)", "name_required": "Se requiere un nombre para mostrar y crear comentarios", - "no_rating": "Sin calificación dada", + "no_rating": "Da una calificación antes de enviar…", "no_reviews_yet": "Aún no hay reseñas. ¡Sé el primero en escribir una y ayuda a los datos abiertos y a los negocios!", "plz_login": "Inicia sesión para dejar una reseña", "posting_as": "Publicación como", @@ -403,7 +431,7 @@ "inviteToSplit": "Dividir esta carretera en segmentos más pequeños. Esto te permite darle propiedades diferentes a partes diferentes de la carretera.", "loginToSplit": "Debes de haber iniciado sesión para dividir una carretera", "split": "Dividir", - "splitTitle": "Escoge una opción en el mapa para dividir esta carretera" + "splitTitle": "Elije en el mapa donde las propiedades de esta carretera cambian" }, "translations": { "activateButton": "Ayuda a traducir MapComplete", @@ -411,7 +439,6 @@ "missing": "{count} cadenas sin traducir", "notImmediate": "Las traducciones no se actualizan directamente. Habitualmente esto lleva unos días" }, - "userinfo": {}, "validation": { "color": { "description": "Un color o código hexadecimal" @@ -423,7 +450,7 @@ "description": "Una orientación" }, "email": { - "description": "dirección-electrónica", + "description": "dirección de correo electrónico", "feedback": "Esta no es una dirección de correo electrónico válida", "noAt": "Una dirección de correo electrónico debe de contener un @" }, diff --git a/langs/fi.json b/langs/fi.json index 4185427de0..d0dfdabb2b 100644 --- a/langs/fi.json +++ b/langs/fi.json @@ -69,8 +69,7 @@ "addPicture": "Lisää kuva", "doDelete": "Poista kuva", "dontDelete": "Peruuta", - "isDeleted": "Poistettu", - "nearbyPictures": {} + "isDeleted": "Poistettu" }, "importInspector": {}, "importLayer": {}, diff --git a/langs/fil.json b/langs/fil.json index 0705f9d568..a5cdcda2a3 100644 --- a/langs/fil.json +++ b/langs/fil.json @@ -79,9 +79,7 @@ "previewbox": {} } }, - "image": { - "nearbyPictures": {} - }, + "image": {}, "importInspector": {}, "importLayer": {}, "index": {}, diff --git a/langs/fr.json b/langs/fr.json index ff672641c5..17bbbc02ce 100644 --- a/langs/fr.json +++ b/langs/fr.json @@ -333,19 +333,6 @@ "doDelete": "Supprimer l'image", "dontDelete": "Annuler", "isDeleted": "Supprimé", - "nearbyPictures": { - "allFiltered": "Aucune image ne correspond à votre filtre", - "browseNearby": "Parcourir les images à proximité…", - "confirm": "L'image sélectionnée représente {title()}", - "hasMatchingPicture": "Est-ce qu'une image correspond à cet objet ? Sélectionnez ci-dessous", - "loadMore": "Charger plus d'images", - "loading": "Chargement des images alentours…", - "noImageSelected": "Sélectionnez une image à lier à l'objet", - "nothingFound": "Aucune image trouvée à proximité…", - "removeFilters": "Cliquez ici pour retirer les filtres", - "title": "Images à proximité", - "withinRadius": "N'afficher que les images prises dans un rayon de {radius} mètres autour de cet objet" - }, "pleaseLogin": "Connectez-vous pour téléverser une photo", "respectPrivacy": "Ne photographiez ni les personnes ni les plaques d'immatriculation. Ne téléversez rien issu de Google Maps, Google Streetview ou d'autre sources soumises à des droits d'auteurs.", "toBig": "Votre image est trop large car elle est de {actual_size}. Veuillez utiliser des images d'au maximum {max_size}", diff --git a/langs/gl.json b/langs/gl.json index ed4dc6c299..23648730bd 100644 --- a/langs/gl.json +++ b/langs/gl.json @@ -138,7 +138,6 @@ "doDelete": "Eliminar imaxe", "dontDelete": "Cancelar", "isDeleted": "Eliminada", - "nearbyPictures": {}, "pleaseLogin": "Inicia a sesión para subir unha imaxe", "respectPrivacy": "Respecta a privacidade. Non fotografes xente ou matrículas", "uploadDone": "A túa imaxe foi engadida. Grazas por axudar.", diff --git a/langs/hu.json b/langs/hu.json index fe39a38f82..de4faf5484 100644 --- a/langs/hu.json +++ b/langs/hu.json @@ -242,7 +242,6 @@ "doDelete": "Kép eltávolítása", "dontDelete": "Mégse", "isDeleted": "Törölve", - "nearbyPictures": {}, "pleaseLogin": "Kép hozzáadásához be kell jelentkezni", "respectPrivacy": "Ne készíts fényképet emberekről és rendszámtáblákról. Ne tölts fel képet a Google Mapsről, a Google Streetview-ról (utcaképről) és egyéb szerzői jog által védett forrásokból.", "toBig": "A kép túl nagy ({actual_size}). Kérjük, legfeljebb {max_size} méretű képeket használj", diff --git a/langs/it.json b/langs/it.json index a1bf685bc6..1506a86b5f 100644 --- a/langs/it.json +++ b/langs/it.json @@ -258,20 +258,6 @@ "doDelete": "Rimuovi immagine", "dontDelete": "Annulla", "isDeleted": "Cancellata", - "nearbyPictures": { - "allFiltered": "Nessuna immagine corrisponde al tuo filtro", - "browseNearby": "Carica immagini nei dintorni…", - "confirm": "L'immagine selezionata mostra {title()}", - "hasMatchingPicture": "C'è un'immagine che corrisponde all'oggetto? Selezionalo sotto", - "loadMore": "Carica più immagini", - "loading": "Caricamento di immagini nei dintorni…", - "noImageSelected": "Seleziona un'immagine per collegarla all'oggetto", - "nothingFound": "Nessuna immagine trovata nei dintorni…", - "onlyTowards": "Mostra solo le immagini scattate verso quest'oggetto", - "removeFilters": "Clicca qui per rimuovere i filtri", - "title": "Immagini nei dintorni", - "withinRadius": "Mostra solo immagini che son scattate entro {radius} metri da questo oggetto" - }, "pleaseLogin": "Accedi per caricare una foto", "respectPrivacy": "Non fotografare persone o targhe dei veicoli. Non caricare da Google Maps, Google Streetview o da altre fonti coperte da copyright.", "toBig": "La tua immagine è troppo grande in quanto è di {actual_size}. Cerca di usare immagini non più grandi di {max_size}", diff --git a/langs/ja.json b/langs/ja.json index 6418961d52..afcfdd6ade 100644 --- a/langs/ja.json +++ b/langs/ja.json @@ -138,7 +138,6 @@ "doDelete": "イメージの削除", "dontDelete": "[キャンセル]", "isDeleted": "削除済み", - "nearbyPictures": {}, "pleaseLogin": "写真を追加するにはログインしてください", "respectPrivacy": "人やナンバープレートを撮影しないでください。Googleマップ、Google Streetview、その他著作権で保護された情報源をアップロードしないでください。", "uploadDone": "あなたの写真が追加されました。ご協力ありがとうございます!", diff --git a/langs/layers/ca.json b/langs/layers/ca.json index 8bcfb911f8..647db4d9e5 100644 --- a/langs/layers/ca.json +++ b/langs/layers/ca.json @@ -351,7 +351,11 @@ }, "artwork-website": { "question": "Hi ha un lloc web amb més informació sobre aquesta obra d'art?", - "render": "Més informació a aquesta pàgina web" + "render": { + "special": { + "text": "Més informació a aquesta pàgina web" + } + } }, "artwork_subject": { "question": "Què representa aquesta obra d'art?", @@ -3160,7 +3164,11 @@ }, "ghost_bike-source": { "question": "En quina pàgina web es pot trobar més informació sobre la bicicleta blanca o l'accident?", - "render": "Més informació disponible" + "render": { + "special": { + "text": "Més informació disponible" + } + } } }, "title": { @@ -3576,8 +3584,7 @@ }, "Email": { "question": "A quina adreça de correu electrònic es pot enviar amb preguntes i problemes amb aquest parc natural?", - "questionHint": "Respecteu la privadesa: només ompliu una adreça de correu electrònic personal si es publica àmpliament", - "render": "{email}" + "questionHint": "Respecteu la privadesa: només ompliu una adreça de correu electrònic personal si es publica àmpliament" }, "Name tag": { "render": "Aquesta àrea s'anomena {name}" @@ -3836,6 +3843,14 @@ }, "parking_spaces": { "tagRenderings": { + "capacity": { + "mappings": { + "0": { + "then": "Aquest espai d'aparcament té 1 plaça." + } + }, + "render": "Aquests espais d'aparcament tenen {capacity} places." + }, "type": { "mappings": { "0": { @@ -5842,7 +5857,7 @@ "verified-mastodon": { "mappings": { "0": { - "then": "S'ha trobat un enllaç al vostre perfil de Mastodon: {_mastodon_link}" + "then": "S'ha trobat un enllaç al vostre perfil de Mastodon: {_mastodon_link}" } } } diff --git a/langs/layers/cs.json b/langs/layers/cs.json index b4e9652c03..4c2da97519 100644 --- a/langs/layers/cs.json +++ b/langs/layers/cs.json @@ -129,7 +129,7 @@ "then": "Volební reklama" }, "4": { - "then": "Informace týkající se divadla, koncertů, ..." + "then": "Informace týkající se divadla, koncertů, …" }, "5": { "then": "Zpráva od neziskových organizací" @@ -351,7 +351,11 @@ }, "artwork-website": { "question": "Existuje webová stránka s dalšími informacemi o tomto uměleckém díle?", - "render": "Více informací na této webové stránce" + "render": { + "special": { + "text": "Více informací na této webové stránce" + } + } }, "artwork_subject": { "question": "Co zobrazuje toto umělecké dílo?", @@ -731,7 +735,7 @@ } }, "question": "Má tato lavička nápis?", - "questionHint": "Např. na připevněné desce, v opěradle, ...", + "questionHint": "Např. na připevněné desce, v opěradle, …", "render": "Tato lavice má následující nápis:

{inscription}

" }, "bench-material": { @@ -956,7 +960,7 @@ "then": "Přítomný je automat, který vydává a přijímá klíče, případně po ověření pravosti a/nebo zaplacení. Jízdní kola jsou zaparkována v blízkosti" }, "5": { - "then": "Jedná se o místo předání, např. vyhrazené parkoviště pro umístění jízdních kol, které je zřetelně označeno jako místo určené pouze pro půjčovnu" + "then": "Jedná se o místo předání, např. vyhrazené parkoviště pro umístění jízdních kol, zřetelně označené jako místo určené pouze pro půjčovnu" } }, "question": "O jakou půjčovnu jízdních kol se jedná?" @@ -1078,6 +1082,11 @@ } }, "question": "Je nabízeno nářadí k opravě vlastního kola?" + }, + "opening_hours": { + "override": { + "question": "Kdy byla tato cyklistická kavárna otevřena?" + } } }, "title": { @@ -1104,7 +1113,7 @@ "then": "Tato mycí služba je bezplatná" }, "1": { - "then": "Tato úklidová služba je placená" + "then": "Využití úklidové služby je zpoplatněno" } }, "question": "Kolik stojí využívání služby mytí?", @@ -1561,7 +1570,46 @@ }, "3": { "then": "Opravy kol {name}" + }, + "4": { + "then": "Prodejna kol {name}" + }, + "5": { + "then": "Oprava kol/obchod {name}" } + }, + "render": "Oprava kol/obchod" + } + }, + "bike_themed_object": { + "description": "Vrstva s objekty s tématikou jízdních kol, které však neodpovídají žádné jiné vrstvě", + "name": "Objekt související s jízdním kolem", + "title": { + "mappings": { + "1": { + "then": "Cyklostezka" + } + }, + "render": "Objekt související s jízdním kolem" + } + }, + "binocular": { + "description": "Dalekohledy", + "name": "Dalekohledy", + "presets": { + "0": { + "description": "Jednooký teleskop nebo dalekohled umístěný na stožáru, který je k dispozici veřejnosti k prohlídce. ", + "title": "dalekohled" + } + }, + "tagRenderings": { + "binocular-charge": { + "mappings": { + "0": { + "then": "Použití zdarma" + } + }, + "question": "Kolik se platí za používání těchto dalekohledů?" } } }, diff --git a/langs/layers/da.json b/langs/layers/da.json index 9cf0a4dcfe..7e5f7982c0 100644 --- a/langs/layers/da.json +++ b/langs/layers/da.json @@ -130,7 +130,11 @@ }, "artwork-website": { "question": "Er der et websted med mere information om dette kunstværk?", - "render": "Yderligere oplysninger på dette websted" + "render": { + "special": { + "text": "Yderligere oplysninger på dette websted" + } + } } }, "title": { @@ -2048,7 +2052,7 @@ } } }, - "7": { + "9": { "options": { "0": { "question": "Gratis at bruge" diff --git a/langs/layers/de.json b/langs/layers/de.json index 3b6a131a9c..42a6eed90f 100644 --- a/langs/layers/de.json +++ b/langs/layers/de.json @@ -351,7 +351,11 @@ }, "artwork-website": { "question": "Auf welcher Webseite gibt es weitere Informationen zum Kunstwerk?", - "render": "Weitere Informationen auf dieser Webseite" + "render": { + "special": { + "text": "Weitere Informationen auf dieser Webseite" + } + } }, "artwork_subject": { "question": "Was zeigt dieses Kunstwerk?", @@ -4224,6 +4228,91 @@ } } }, + "elongated_coin": { + "description": "Ebene mit Münzpressen.", + "name": "Münzpressen", + "presets": { + "0": { + "title": "Eine Münzpresse" + } + }, + "tagRenderings": { + "charge": { + "freeform": { + "placeholder": "Einwurf (z.B. 0,5€)" + }, + "mappings": { + "0": { + "then": "Eine Münze zu Pressen kostet 1 Euro." + }, + "1": { + "then": "Eine Münze zu Pressen kostet 2€." + } + }, + "question": "Wieviel kostet es eine Münze zu Pressen?", + "render": "Es kostet {charge}€ um eine Münze zu Pressen." + }, + "coin": { + "freeform": { + "placeholder": "Münzenart (z.B. 10 Cent)" + }, + "mappings": { + "0": { + "then": "Die Münzpresse benötigt eine 2 Cent Münze um zu Pressen." + }, + "1": { + "then": "Die Münzpresse benötigt eine 5 Cent Münze um zu Pressen." + }, + "2": { + "then": "Die Münzpresse benötigt eine 10 Cent Münze um zu Pressen." + }, + "3": { + "then": "Die Münzpresse benötigt eine 25 Cent Münze um zu Pressen." + }, + "4": { + "then": "Die Münzpresse benötigt eine 50 Cent Münze um zu Pressen." + } + }, + "question": "Welche Münze wird zum Pressen verwendet?", + "render": "Die Münzpresse benötigt eine {coin:type} Münze um zu Pressen." + }, + "designs": { + "freeform": { + "placeholder": "Motivanzahl (z.B. 5)" + }, + "mappings": { + "0": { + "then": "Die Münzpresse hat ein Motiv zur Auswahl." + }, + "1": { + "then": "Die Münzpresse hat zwei Motive zur Auswahl." + }, + "2": { + "then": "Die Münzpresse hat drei Motive zur Auswahl." + }, + "3": { + "then": "Die Münzpresse hat vier Motive zur Auswahl." + } + }, + "question": "Wieviele Motive sind verfügbar?", + "render": "Die Münzpresse hat {coin:design_count} Motive zur Auswahl." + }, + "indoor": { + "mappings": { + "0": { + "then": "Die Münzpresse befindet sich im Inneren." + }, + "1": { + "then": "Die Münzpresse befindet sich Draußen." + } + }, + "question": "Befindet sich die Münzpresse im Inneren?" + } + }, + "title": { + "render": "Münzpresse" + } + }, "entrance": { "description": "Eine Ebene, die Eingänge anzeigt und die Möglichkeit bietet, weitere Daten zu erheben, die z. B. für Rollstuhlfahrer wichtig sind (aber auch für Radfahrer, Lieferpersonal, …)", "name": "Eingänge", @@ -4438,7 +4527,7 @@ } } }, - "3": { + "5": { "options": { "0": { "question": "Mit und ohne Bild" @@ -4451,14 +4540,14 @@ } } }, - "4": { + "6": { "options": { "0": { "question": "Mit taktilem Pflaster" } } }, - "5": { + "7": { "options": { "0": { "question": "Mit oder ohne taktiles Pflaster" @@ -4474,14 +4563,14 @@ } } }, - "6": { + "8": { "options": { "0": { "question": "Bio-Produkte im Angebot" } } }, - "7": { + "9": { "options": { "0": { "question": "Nutzung kostenlos" @@ -5066,7 +5155,11 @@ }, "ghost_bike-source": { "question": "Auf welcher Webseite kann man mehr Informationen über das Geisterrad oder den Unfall finden?", - "render": "Mehr Informationen" + "render": { + "special": { + "text": "Mehr Informationen" + } + } }, "ghost_bike-start_date": { "question": "Wann wurde dieses Geisterrad aufgestellt?", @@ -5982,8 +6075,7 @@ }, "Email": { "question": "An welche Email-Adresse kann man sich bei Fragen und Problemen zu diesem Gebiet wenden?", - "questionHint": "Respektieren Sie die Privatsphäre. Geben Sie nur dann eine persönliche Email-Adresse an, wenn diese allgemein bekannt ist", - "render": "{email}" + "questionHint": "Respektieren Sie die Privatsphäre. Geben Sie nur dann eine persönliche Email-Adresse an, wenn diese allgemein bekannt ist" }, "Name tag": { "mappings": { @@ -7016,7 +7108,11 @@ }, "public_bookcase-website": { "question": "Auf welcher Webseite findet man Informationen zu diesem Bücherschrank?", - "render": "Weitere Informationen auf der Webseite" + "render": { + "special": { + "text": "Weitere Informationen auf der Webseite" + } + } } }, "title": { @@ -7030,6 +7126,15 @@ }, "questions": { "tagRenderings": { + "check_date": { + "mappings": { + "0": { + "then": "Dieses Objekt wurde heute zuletzt kontrolliert" + } + }, + "question": "Wann wurde dieses Objekt zuletzt kontrolliert?", + "render": "Dieses Objekt wurde zuletzt kontrolliert am {check_date}" + }, "denominations-coins": { "mappings": { "0": { @@ -9547,10 +9652,10 @@ "verified-mastodon": { "mappings": { "0": { - "then": "Es wurde ein Link zu deinem Mastodon-Profil gefunden: {_mastodon_link}" + "then": "Es wurde ein Link zu deinem Mastodon-Profil gefunden: {_mastodon_link}" }, "1": { - "then": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. Bearbeiten Sie Ihre Profilbeschreibung und fügen Sie dort Folgendes ein: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" + "then": "Wir haben einen Link gefunden, der aussieht wie ein Mastodon-Konto, aber nicht verifiziert ist. Bearbeiten Sie Ihre Profilbeschreibung und fügen Sie dort Folgendes ein: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" } } } diff --git a/langs/layers/en.json b/langs/layers/en.json index 4c354d99eb..0e180493ef 100644 --- a/langs/layers/en.json +++ b/langs/layers/en.json @@ -351,7 +351,11 @@ }, "artwork-website": { "question": "Is there a website with more information about this artwork?", - "render": "More information on this website" + "render": { + "special": { + "text": "More information on this website" + } + } }, "artwork_subject": { "question": "What does this artwork depict?", @@ -4224,6 +4228,91 @@ } } }, + "elongated_coin": { + "description": "Layer showing penny presses.", + "name": "Penny Presses", + "presets": { + "0": { + "title": "a penny press" + } + }, + "tagRenderings": { + "charge": { + "freeform": { + "placeholder": "Cost (e.g. 0.50 EUR)" + }, + "mappings": { + "0": { + "then": "It costs 1 euro to press a penny." + }, + "1": { + "then": "It costs 2 euros to press a penny." + } + }, + "question": "How much does it cost to press a penny?", + "render": "It costs {charge} to press a penny." + }, + "coin": { + "freeform": { + "placeholder": "Coin type (e.g. 10cent)" + }, + "mappings": { + "0": { + "then": "This penny press uses a 2 cent coin for pressing." + }, + "1": { + "then": "This penny press uses a 5 cent coin for pressing." + }, + "2": { + "then": "This penny press uses a 10 cent coin for pressing." + }, + "3": { + "then": "This penny press uses a 25 cent coin for pressing." + }, + "4": { + "then": "This penny press uses a 50 cent coin for pressing." + } + }, + "question": "What coin is used for pressing?", + "render": "This penny press uses a {coin:type} coin for pressing." + }, + "designs": { + "freeform": { + "placeholder": "Number of designs (e.g. 5)" + }, + "mappings": { + "0": { + "then": "This penny press has one design available." + }, + "1": { + "then": "This penny press has two designs available." + }, + "2": { + "then": "This penny press has three designs available." + }, + "3": { + "then": "This penny press has four designs available." + } + }, + "question": "How many designs are available?", + "render": "This penny press has {coin:design_count} designs available." + }, + "indoor": { + "mappings": { + "0": { + "then": "This penny press is located indoors." + }, + "1": { + "then": "This penny press is located outdoors." + } + }, + "question": "Is the penny press indoors?" + } + }, + "title": { + "render": "Penny Press" + } + }, "entrance": { "description": "A layer showing entrances and offering capabilities to survey some advanced data which is important for e.g. wheelchair users (but also bicycle users, people who want to deliver, …)", "name": "Entrance", @@ -4439,6 +4528,20 @@ } }, "3": { + "options": { + "0": { + "question": "Accepts debit cards" + } + } + }, + "4": { + "options": { + "0": { + "question": "Accepts credit cards" + } + } + }, + "5": { "options": { "0": { "question": "With and without images" @@ -4451,14 +4554,14 @@ } } }, - "4": { + "6": { "options": { "0": { "question": "With tactile paving" } } }, - "5": { + "7": { "options": { "0": { "question": "With or without tactile paving" @@ -4474,14 +4577,14 @@ } } }, - "6": { + "8": { "options": { "0": { "question": "Has organic options" } } }, - "7": { + "9": { "options": { "0": { "question": "Free to use" @@ -5072,7 +5175,11 @@ }, "ghost_bike-source": { "question": "On what webpage can one find more info about the ghost bike or the accident?", - "render": "More info available" + "render": { + "special": { + "text": "More info available" + } + } }, "ghost_bike-start_date": { "question": "When was this Ghost bike installed?", @@ -5988,8 +6095,7 @@ }, "Email": { "question": "What email adress can one send to with questions and problems with this nature reserve?", - "questionHint": "Respect privacy - only fill out a personal email address if this is widely published", - "render": "{email}" + "questionHint": "Respect privacy - only fill out a personal email address if this is widely published" }, "Name tag": { "mappings": { @@ -6784,6 +6890,20 @@ } }, "tagRenderings": { + "has_atm": { + "mappings": { + "0": { + "then": "This post office has an ATM" + }, + "1": { + "then": "This post office does not have an ATM" + }, + "2": { + "then": "This post office does have an ATM, but it is mapped as a different icon" + } + }, + "question": "Does this post office have an ATM?" + }, "letter-from": { "mappings": { "0": { @@ -7022,7 +7142,11 @@ }, "public_bookcase-website": { "question": "Is there a website with more information about this public bookcase?", - "render": "More info on the website" + "render": { + "special": { + "text": "More info on the website" + } + } } }, "title": { @@ -7036,6 +7160,15 @@ }, "questions": { "tagRenderings": { + "check_date": { + "mappings": { + "0": { + "then": "This object was last checked today" + } + }, + "question": "When was this object last checked?", + "render": "This object was last checked on {check_date}" + }, "denominations-coins": { "mappings": { "0": { @@ -7219,6 +7352,9 @@ }, "question": "Is this object lit or does it emit light?" }, + "mastodon": { + "question": "What is the Mastodon-handle of {title()}?" + }, "multilevels": { "override": { "question": "What levels does this elevator go to?", @@ -9553,10 +9689,10 @@ "verified-mastodon": { "mappings": { "0": { - "then": "A link to your Mastodon-profile has been been found: {_mastodon_link}" + "then": "A link to your Mastodon-profile has been been found: {_mastodon_link}" }, "1": { - "then": "We found a link to what looks to be a mastodon account, but it is unverified. Edit your profile description and place the following there: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" + "then": "We found a link to what looks to be a mastodon account, but it is unverified. Edit your profile description and place the following there: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" } } } @@ -9701,6 +9837,15 @@ }, "15": { "then": "Flowers are sold" + }, + "16": { + "then": "Parking tickets are sold" + }, + "17": { + "then": "Pressed pennies are sold" + }, + "18": { + "then": "Public transport tickets are sold" } }, "question": "What does this vending machine sell?", diff --git a/langs/layers/es.json b/langs/layers/es.json index 0eb28931bd..984e8131ef 100644 --- a/langs/layers/es.json +++ b/langs/layers/es.json @@ -129,7 +129,7 @@ "then": "Publicidad electoral" }, "4": { - "then": "Información sobre teatros, conciertos, ..." + "then": "Información sobre teatros, conciertos, …" }, "5": { "then": "Mensaje de organizaciones sin ánimo de lucro" @@ -144,7 +144,7 @@ "then": "Cartel de financiación" }, "9": { - "then": "un mapa" + "then": "Un mapa" } }, "question": "Que tipo de mensaje se muestra?" @@ -351,7 +351,11 @@ }, "artwork-website": { "question": "¿Hay un sitio web con más información sobre esta obra de arte?", - "render": "Más información en este sitio web" + "render": { + "special": { + "text": "Más información en este sitio web" + } + } }, "artwork_subject": { "question": "¿Qué representa esta obra de arte?", @@ -799,7 +803,7 @@ } }, "question": "¿Todavía funciona esta máquina expendedora?", - "render": "El estado operacional es {operational_status}" + "render": "El estado operacional es {operational_status}" } } }, @@ -886,7 +890,7 @@ "then": "Bolardo" }, "7": { - "then": "Una área en el suelo que está marcada para el aparcamiento de bicicletas" + "then": "Una área en el suelo que está marcada para el aparcamiento de bicicletas" } }, "question": "¿Cual es el tipo de este aparcamiento de bicicletas?", @@ -1392,7 +1396,7 @@ "then": "Este es un club nocturno o discoteca centrado en bailar, música de un DJ con un espectáculo de luces que la acompaña y un bar donde conseguir bebidas (alcohólicas)" } }, - "question": "Qué tipo de cafetería es esta" + "question": "Qué tipo de cafe es este?" }, "Name": { "question": "¿Cual es el nombre de este pub?", @@ -1405,7 +1409,7 @@ "then": "{name}" } }, - "render": "Pub" + "render": "Bar" } }, "charging_station": { @@ -1735,11 +1739,11 @@ } }, "question": "¿Qué corriente ofrecen los conectores con
USB para cargar teléfonos y dispositivos electrónicos pequeños
?", - "render": "
USB para carga teléfonos y dispositivos electrónicos pequeños
salida de hasta {socket:USB-A:current}A" + "render": "
USB para carga teléfonos y dispositivos electrónicos pequeños
salida de hasta {socket:USB-A:current}A" }, "email": { "question": "¿Cual es la dirección de correo electrónico de esta operadora?", - "render": "En caso de problemas, envía un correo electrónico a {email}" + "render": "En caso de problemas, envía un correo electrónico a {email}" }, "fee": { "mappings": { @@ -2401,6 +2405,91 @@ "render": "Agua potable" } }, + "elongated_coin": { + "description": "Capa mostrando prensas de centavo.", + "name": "Prensas de centavo", + "presets": { + "0": { + "title": "una prensa de centavo" + } + }, + "tagRenderings": { + "charge": { + "freeform": { + "placeholder": "Costo (por ejemplo, 0.50 euros)" + }, + "mappings": { + "0": { + "then": "Cuesta 1 euro para presionar un centavo." + }, + "1": { + "then": "Cuesta 2 euros para presionar un centavo." + } + }, + "question": "¿Cuánto cuesta presionar un centavo?", + "render": "Cuesta {charge} para presionar un centavo." + }, + "coin": { + "freeform": { + "placeholder": "Tipo de moneda (por ejemplo, 10 centavos)" + }, + "mappings": { + "0": { + "then": "Esta prensa de centavo utiliza una moneda de 2 centavos para presionar." + }, + "1": { + "then": "Esta prensa de centavo utiliza una moneda de 5 centavos para presionar." + }, + "2": { + "then": "Esta prensa de centavo utiliza una moneda de 10 centavos para presionar." + }, + "3": { + "then": "Esta prensa de centavo utiliza una moneda de 25 centavos para presionar." + }, + "4": { + "then": "Esta prensa de centavo utiliza una moneda de 50 centavos para presionar." + } + }, + "question": "Qué moneda se utiliza para presionar?", + "render": "Esta prensa de centavo utiliza una moneda {coin:type} para presionar." + }, + "designs": { + "freeform": { + "placeholder": "Número de diseños (por ejemplo, 5)" + }, + "mappings": { + "0": { + "then": "Esta prensa tiene un diseño disponible." + }, + "1": { + "then": "Esta prensa tiene dos diseños disponibles." + }, + "2": { + "then": "Esta prensa tiene tres diseños disponibles." + }, + "3": { + "then": "Esta prensa tiene cuatro diseños disponibles." + } + }, + "question": "Cuántos diseños son disponibles?", + "render": "Esta prensa tiene {coin:design_count} diseños disponibles." + }, + "indoor": { + "mappings": { + "0": { + "then": "Esta prensa está ubicada en interior." + }, + "1": { + "then": "Esta prensa está ubicada al aire libre." + } + }, + "question": "La prensa de centavo esta al interior?" + } + }, + "title": { + "render": "Prensa de centavo" + } + }, "entrance": { "description": "Una capa que muestra capas y ofrece la posibilidad de sondear algunos datos avanzados que son importantes para, por ejemplo, usuarios de sillas de ruedas (pero también incluye ciclistas, gente que quiere repartir, ...)", "name": "Entrada", @@ -2588,7 +2677,7 @@ } } }, - "description": "Una capa que muestra restaurantes y locales de comida rápida (con un renderizado especial para freidurías)", + "description": "Una capa mostrando restaurantes y locales de comida rápida (con un renderizado especial para friterías)", "filter": { "2": { "options": { @@ -2776,7 +2865,7 @@ } }, "ghost_bike": { - "name": "Bicicleta blanca", + "name": "Bicicletas blanca", "presets": { "0": { "title": "una bicicleta blanca" @@ -2962,7 +3051,7 @@ "name": "Velocidad", "tagRenderings": { "maxspeed-maxspeed": { - "question": "Qué velocidad tiene" + "question": "Qué es la velocidad máxima legal uno está permitido conducir en esta carretera?" } }, "units": { @@ -3353,7 +3442,7 @@ } }, "postboxes": { - "description": "La capa que muestra buzones de correo.", + "description": "La capa que mostrando buzones de correo.", "name": "Buzones de correo", "presets": { "0": { @@ -4323,7 +4412,7 @@ "Surveillance type: public, outdoor, indoor": { "mappings": { "0": { - "then": "Es un área pública, como una calle, un puente, una plaza, un parque, una estación de tren, un corredor público o túnel, ..." + "then": "Es un área pública, como una calle, un puente, una plaza, un parque, una estación de tren, un corredor público o túnel, …" }, "1": { "then": "Es un área exterior pero privada (ej: estacionamiento, gasolinera, patio, entrada, camino privado, ...)" diff --git a/langs/layers/fr.json b/langs/layers/fr.json index dd622158d7..37b9aa295e 100644 --- a/langs/layers/fr.json +++ b/langs/layers/fr.json @@ -335,7 +335,11 @@ }, "artwork-website": { "question": "Existe-t-il un site web où trouver plus d'informations sur cette œuvre d'art ?", - "render": "Plus d'info sûr ce site web" + "render": { + "special": { + "text": "Plus d'info sûr ce site web" + } + } }, "artwork_subject": { "question": "Que représente cette oeuvre d'art ?", @@ -2943,6 +2947,15 @@ } } }, + "elongated_coin": { + "tagRenderings": { + "charge": { + "freeform": { + "placeholder": "Coût (par ex. 0.50 EUR)" + } + } + } + }, "entrance": { "description": "Une couche montrant les entrées et offrant des capacités pour étudier certaines données avancées qui sont importantes, par exemple. les utilisateurs de fauteuils roulants (mais aussi les utilisateurs de vélos, les personnes qui veulent faire des livraisons, …)", "name": "Entrée", @@ -3154,14 +3167,14 @@ } } }, - "4": { + "6": { "options": { "0": { "question": "Avec revêtement podotactile" } } }, - "5": { + "7": { "options": { "0": { "question": "Avec ou sans revêtement podotactile" @@ -3177,7 +3190,7 @@ } } }, - "7": { + "9": { "options": { "0": { "question": "Utilisation gratuite" @@ -3575,7 +3588,11 @@ }, "ghost_bike-source": { "question": "Sur quelle page web peut-on trouver plus d'informations sur le Vélo fantôme ou l'accident ?", - "render": "Plus d'informations sont disponibles" + "render": { + "special": { + "text": "Plus d'informations sont disponibles" + } + } }, "ghost_bike-start_date": { "question": "Quand ce vélo fantôme a-t-il été installée ?", @@ -3866,8 +3883,7 @@ }, "Email": { "question": "À quelle adresse courriel peut-on envoyer des questions et des problèmes concernant cette réserve naturelle ? ", - "questionHint": "Respecter la vie privée – renseignez une adresse électronique personnelle seulement si celle-ci est largement publiée", - "render": "{email}" + "questionHint": "Respecter la vie privée – renseignez une adresse électronique personnelle seulement si celle-ci est largement publiée" }, "Surface area": { "render": "Superficie : {_surface:ha} ha" @@ -4256,7 +4272,11 @@ }, "public_bookcase-website": { "question": "Y a-t-il un site web avec plus d'informations sur cette microbibliothèque ?", - "render": "Plus d'infos sur le site web" + "render": { + "special": { + "text": "Plus d'infos sur le site web" + } + } } }, "title": { @@ -5991,7 +6011,7 @@ "verified-mastodon": { "mappings": { "0": { - "then": "Un lien vers votre profil Mastodon a été trouvé : {_mastodon_link}" + "then": "Un lien vers votre profil Mastodon a été trouvé : {_mastodon_link}" } } } diff --git a/langs/layers/hu.json b/langs/layers/hu.json index 033b17ec54..44c9e796cd 100644 --- a/langs/layers/hu.json +++ b/langs/layers/hu.json @@ -130,7 +130,11 @@ }, "artwork-website": { "question": "Van-e olyan honlap, amely további információkat tartalmaz erről a műalkotásról?", - "render": "További információ ezen a weboldalon" + "render": { + "special": { + "text": "További információ ezen a weboldalon" + } + } } }, "title": { @@ -752,7 +756,11 @@ }, "public_bookcase-website": { "question": "Van-e olyan weboldal, ahol további információ található erről a nyilvános könyvespolcról?", - "render": "További információ ezen a weboldalon" + "render": { + "special": { + "text": "További információ ezen a weboldalon" + } + } } }, "title": { diff --git a/langs/layers/id.json b/langs/layers/id.json index 950dc3170e..ff82e7cdd1 100644 --- a/langs/layers/id.json +++ b/langs/layers/id.json @@ -81,7 +81,11 @@ }, "artwork-website": { "question": "Adakah situs web mengenai informasi lebih lanjut tentang karya seni ini?", - "render": "Info lanjut tersedia di laman web ini" + "render": { + "special": { + "text": "Info lanjut tersedia di laman web ini" + } + } } }, "title": { @@ -341,7 +345,11 @@ "render": "{inscription}" }, "ghost_bike-source": { - "render": "Informasi lanjut tersedia" + "render": { + "special": { + "text": "Informasi lanjut tersedia" + } + } } } }, @@ -365,13 +373,6 @@ } } }, - "nature_reserve": { - "tagRenderings": { - "Email": { - "render": "{email}" - } - } - }, "playground": { "tagRenderings": { "playground-email": { diff --git a/langs/layers/it.json b/langs/layers/it.json index ea7d06fcce..84bc390b5e 100644 --- a/langs/layers/it.json +++ b/langs/layers/it.json @@ -105,7 +105,11 @@ }, "artwork-website": { "question": "Esiste un sito web con maggiori informazioni su quest’opera?", - "render": "Ulteriori informazioni su questo sito web" + "render": { + "special": { + "text": "Ulteriori informazioni su questo sito web" + } + } } }, "title": { @@ -1344,7 +1348,11 @@ }, "ghost_bike-source": { "question": "In quale pagina web si possono trovare informazioni sulla bici fantasma o l’incidente?", - "render": "Sono disponibili ulteriori informazioni" + "render": { + "special": { + "text": "Sono disponibili ulteriori informazioni" + } + } }, "ghost_bike-start_date": { "question": "Quando è stata installata questa bici fantasma?", @@ -1509,8 +1517,7 @@ }, "Email": { "question": "Qual è l’indirizzo email a cui scrivere per fare domande o segnalare problemi su questa riserva naturale?", - "questionHint": "Rispetta la privacy (compila l’indirizzo email personale solo se è stato reso pubblico)", - "render": "{email}" + "questionHint": "Rispetta la privacy (compila l’indirizzo email personale solo se è stato reso pubblico)" }, "Surface area": { "render": "Area: {_surface:ha} ha" @@ -1759,7 +1766,11 @@ }, "public_bookcase-website": { "question": "C'è un sito web con maggiori informazioni su questa microbiblioteca?", - "render": "Maggiori informazioni sul sito web" + "render": { + "special": { + "text": "Maggiori informazioni sul sito web" + } + } } }, "title": { diff --git a/langs/layers/ja.json b/langs/layers/ja.json index 71997d0530..eb2c414f2a 100644 --- a/langs/layers/ja.json +++ b/langs/layers/ja.json @@ -105,7 +105,11 @@ }, "artwork-website": { "question": "この作品についての詳しい情報はどのウェブサイトにありますか?", - "render": "Webサイトに詳細情報がある" + "render": { + "special": { + "text": "Webサイトに詳細情報がある" + } + } } }, "title": { diff --git a/langs/layers/nb_NO.json b/langs/layers/nb_NO.json index cb139d8ecf..d073d3e7e1 100644 --- a/langs/layers/nb_NO.json +++ b/langs/layers/nb_NO.json @@ -101,7 +101,11 @@ }, "artwork-website": { "question": "Finnes det en nettside med mer info om dette kunstverket?", - "render": "Mer info er å finne på denne nettsiden" + "render": { + "special": { + "text": "Mer info er å finne på denne nettsiden" + } + } }, "artwork_subject": { "render": "Dette kunstverket viser {wikidata_label(subject:wikidata)}{wikipedia(subject:wikidata)}" diff --git a/langs/layers/nl.json b/langs/layers/nl.json index 8f3dcff9a6..4fa39eed57 100644 --- a/langs/layers/nl.json +++ b/langs/layers/nl.json @@ -47,6 +47,9 @@ "8": { "description": "Een stuk groot, weerbestendig textiel met opgedrukte reclameboodschap die permanent aan de muur hangt", "title": "een spandoek" + }, + "12": { + "title": "een muurschildering" } }, "tagRenderings": { @@ -61,6 +64,12 @@ }, "message_type": { "mappings": { + "0": { + "then": "Commerciële boodschap" + }, + "1": { + "then": "Lokale informatie" + }, "4": { "then": "Informatie over cultuurevenementen zoals theaters, optredens, …" }, @@ -238,7 +247,11 @@ }, "artwork-website": { "question": "Is er een website met meer informatie over dit kunstwerk?", - "render": "Meer informatie op deze website" + "render": { + "special": { + "text": "Meer informatie op deze website" + } + } }, "artwork_subject": { "question": "Wat beeldt dit kunstwerk af?", @@ -4305,7 +4318,7 @@ "0": { "options": { "0": { - "question": "Nu geopened" + "question": "Nu open" } } }, @@ -4323,7 +4336,7 @@ } } }, - "3": { + "5": { "options": { "0": { "question": "Met en zonder afbeelding" @@ -4336,14 +4349,14 @@ } } }, - "4": { + "6": { "options": { "0": { "question": "Met voelbare bestrating" } } }, - "5": { + "7": { "options": { "0": { "question": "Met of zonder voelbare bestrating" @@ -4359,14 +4372,14 @@ } } }, - "6": { + "8": { "options": { "0": { "question": "Heeft biologische opties" } } }, - "7": { + "9": { "options": { "0": { "question": "Gratis toegankelijk" @@ -4842,7 +4855,11 @@ }, "ghost_bike-source": { "question": "Op welke website kan men meer informatie vinden over de Witte fiets of over het ongeval?", - "render": "Meer informatie" + "render": { + "special": { + "text": "Meer informatie" + } + } }, "ghost_bike-start_date": { "question": "Wanneer werd deze witte fiets geplaatst?", @@ -5669,8 +5686,7 @@ }, "Email": { "question": "Waar kan men naartoe emailen voor vragen en meldingen van dit natuurgebied?", - "questionHint": "Respecteer privacy - geef enkel persoonlijke emailadressen als deze elders zijn gepubliceerd", - "render": "{email}" + "questionHint": "Respecteer privacy - geef enkel persoonlijke emailadressen als deze elders zijn gepubliceerd" }, "Name tag": { "mappings": { @@ -6452,6 +6468,20 @@ } }, "tagRenderings": { + "has_atm": { + "mappings": { + "0": { + "then": "Dit postkantoor heeft een bankautomaat" + }, + "1": { + "then": "Dit postkantoor heeft geen bankautomaaat" + }, + "2": { + "then": "Dit postkantoor heeft een bankautomaat, maar deze staat apart op de kaart aangeduid" + } + }, + "question": "Heeft dit postkantoor een bankautomaat?" + }, "letter-from": { "mappings": { "0": { @@ -6644,7 +6674,11 @@ }, "public_bookcase-website": { "question": "Is er een website over dit boekenruilkastje?", - "render": "Meer info op de website" + "render": { + "special": { + "text": "Meer info op de website" + } + } } }, "title": { @@ -6658,6 +6692,15 @@ }, "questions": { "tagRenderings": { + "check_date": { + "mappings": { + "0": { + "then": "Dit object is vandaag voor het laatst gecontroleerd" + } + }, + "question": "Wanneer is dit object voor het laatst gecontroleerd?", + "render": "Dit object is voor het laatst gecontroleerd op {check_date}" + }, "denominations-coins": { "mappings": { "0": { @@ -8908,10 +8951,10 @@ "verified-mastodon": { "mappings": { "0": { - "then": "Een link naar je Mastodon-profiel werd gevonden: {_mastodon_link}" + "then": "Een link naar je Mastodon-profiel werd gevonden: {_mastodon_link}" }, "1": { - "then": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.Pas je profielbeschrijving aan en plaats er de volgende code: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" + "then": "Je profielbeschrijving bevat een link die vermoedelijk naar je Mastodon gaat, maar deze link is niet verifieerdbaar voor Mastodon.Pas je profielbeschrijving aan en plaats er de volgende code: <a href=\"{_mastodon_candidate}\" rel=\"me\">Mastodon</a>" } } } @@ -9056,6 +9099,12 @@ }, "15": { "then": "Bloemen worden verkocht" + }, + "16": { + "then": "Parkeerkaarten worden verkocht" + }, + "18": { + "then": "Openbaar vervoerkaartjes worden verkocht" } }, "question": "Wat verkoopt deze verkoopautomaat?", diff --git a/langs/layers/pl.json b/langs/layers/pl.json index 39de5004f6..8be04a2f74 100644 --- a/langs/layers/pl.json +++ b/langs/layers/pl.json @@ -79,7 +79,11 @@ }, "artwork-website": { "question": "Gdzie znajdę więcej informacji na temat tego dzieła sztuki?", - "render": "Więcej informacji na tej stronie" + "render": { + "special": { + "text": "Więcej informacji na tej stronie" + } + } } }, "title": { diff --git a/langs/layers/pt.json b/langs/layers/pt.json index c9719c9684..0b555ee2de 100644 --- a/langs/layers/pt.json +++ b/langs/layers/pt.json @@ -233,7 +233,11 @@ }, "artwork-website": { "question": "Existe um site com mais informações sobre esta obra de arte?", - "render": "Mais informações neste site" + "render": { + "special": { + "text": "Mais informações neste site" + } + } }, "artwork_subject": { "question": "O que esta obra de arte representa?", diff --git a/langs/layers/ru.json b/langs/layers/ru.json index a320c85f53..b32efc0293 100644 --- a/langs/layers/ru.json +++ b/langs/layers/ru.json @@ -117,7 +117,11 @@ }, "artwork-website": { "question": "Есть ли сайт с более подробной информацией об этой работе?", - "render": "Больше информации на этом сайте" + "render": { + "special": { + "text": "Больше информации на этом сайте" + } + } } }, "title": { @@ -996,7 +1000,11 @@ "render": "В знак памяти о {subject}" }, "ghost_bike-source": { - "render": "Доступна более подробная информация" + "render": { + "special": { + "text": "Доступна более подробная информация" + } + } }, "ghost_bike-start_date": { "render": "Установлен {start_date}" @@ -1295,13 +1303,6 @@ } } }, - "nature_reserve": { - "tagRenderings": { - "Email": { - "render": "{email}" - } - } - }, "observation_tower": { "name": "Смотровые башни", "title": { @@ -1515,7 +1516,11 @@ }, "public_bookcase-website": { "question": "Есть ли веб-сайт с более подробной информацией об этом общественном книжном шкафе?", - "render": "Более подробная информация на сайте" + "render": { + "special": { + "text": "Более подробная информация на сайте" + } + } } }, "title": { diff --git a/langs/layers/zh_Hant.json b/langs/layers/zh_Hant.json index 82fb3ca3bb..1b07031cf0 100644 --- a/langs/layers/zh_Hant.json +++ b/langs/layers/zh_Hant.json @@ -72,7 +72,11 @@ }, "artwork-website": { "question": "在那個網站能夠找到更多藝術品的資訊?", - "render": "這個網站有更多資訊" + "render": { + "special": { + "text": "這個網站有更多資訊" + } + } } }, "title": { diff --git a/langs/nb_NO.json b/langs/nb_NO.json index 74da37d95b..3c56e2c009 100644 --- a/langs/nb_NO.json +++ b/langs/nb_NO.json @@ -300,17 +300,6 @@ "doDelete": "Fjern bilde", "dontDelete": "Avbryt", "isDeleted": "Slettet", - "nearbyPictures": { - "allFiltered": "Ingen bilder samsvarte", - "browseNearby": "Utforsk bilder i nærheten …", - "confirm": "Valgt bilde viser {title()}", - "loadMore": "Last inn flere bilder", - "loading": "Laster inn bilder i nærheten …", - "noImageSelected": "Velg et bilde for å knytte det til objektet", - "nothingFound": "Fant ingen bilder i nærheten …", - "removeFilters": "Klikk her for å fjerne filterne", - "title": "Bilder i nærheten" - }, "pleaseLogin": "Logg inn for å legge til et bilde", "respectPrivacy": "Ikke ta bilder av folk eller bilskilt. Ikke last opp Google Maps, Google Streetview eller andre opphavsrettsbeskyttede kilder.", "toBig": "Bildet ditt på {actual_size} er for stort. Det kan maksimalt være {max_size}.", diff --git a/langs/nl.json b/langs/nl.json index 1a82b45e72..1a7e651cb5 100644 --- a/langs/nl.json +++ b/langs/nl.json @@ -404,20 +404,6 @@ "doDelete": "Verwijder afbeelding", "dontDelete": "Annuleren", "isDeleted": "Verwijderd", - "nearbyPictures": { - "allFiltered": "Geen enkele afbeelding voldoet aan je filter", - "browseNearby": "Bekijk afbeeldingen van deze buurt…", - "confirm": "De geselecteerde afbeelding toont {title()}", - "hasMatchingPicture": "Is er een afbeelding die dit object toont? Selecteer deze hieronder:", - "loadMore": "Toon meer afbeeldingen", - "loading": "Afbeeldingen worden geladen…", - "noImageSelected": "Selecteer een afbeelding om deze te linken", - "nothingFound": "Geen afbeeldingen van deze buurt gevonden…", - "onlyTowards": "Toon enkel afbeeldingen die naar dit object kijken", - "removeFilters": "Verwijder alle filters", - "title": "Afbeeldingen van de buurt", - "withinRadius": "Toon enkel afbeeldingen die gemaakt zijn binnen {radius} meter van dit object" - }, "pleaseLogin": "Gelieve je aan te melden om een foto toe te voegen", "respectPrivacy": "Fotografeer geen mensen of nummerplaten. Voeg geen Google Maps, Google Streetview of foto's met auteursrechten toe.", "toBig": "Je afbeelding is te groot, namelijk {actual_size}. Gelieve afbeeldingen van maximaal {max_size} te gebruiken", diff --git a/langs/pa_PK.json b/langs/pa_PK.json index 15de79618e..48e25c330c 100644 --- a/langs/pa_PK.json +++ b/langs/pa_PK.json @@ -85,12 +85,7 @@ "addPicture": "تصویر پایو", "doDelete": "تصویر ہٹاؤ", "dontDelete": "رد کرو", - "isDeleted": "مٹائی گئی", - "nearbyPictures": { - "browseNearby": "نیڑے تیڑے تے تصویراں ویکھو۔ ۔ ۔", - "loadMore": "ہور تصویراں لوڈ کرو", - "title": "نیڑے تیڑے تے تصویراں" - } + "isDeleted": "مٹائی گئی" }, "move": { "cancel": "چلݨ رد کرو" diff --git a/langs/pl.json b/langs/pl.json index cfa569f962..ad4c65ebca 100644 --- a/langs/pl.json +++ b/langs/pl.json @@ -139,9 +139,6 @@ "doDelete": "Usuń ilustrację", "dontDelete": "Anuluj", "isDeleted": "Usunięte", - "nearbyPictures": { - "loading": "Wczytywanie obrazów w pobliżu…" - }, "pleaseLogin": "Zaloguj się, by dodać zdjęcie", "respectPrivacy": "Nie fotografuj ludzi i tablic rejestracyjnych. Nie wysyłaj także treści z Google Maps, Google Streetview ani innych licencjonowanych źródeł.", "uploadDone": "Twoje zdjęcie zostało dodane. Dzięki za pomoc!", diff --git a/langs/pt.json b/langs/pt.json index bc70ecd629..1b2767540c 100644 --- a/langs/pt.json +++ b/langs/pt.json @@ -297,20 +297,6 @@ "doDelete": "Remover imagem", "dontDelete": "Cancelar", "isDeleted": "Eliminada", - "nearbyPictures": { - "allFiltered": "Nenhuma imagem correspondeu ao seu filtro", - "browseNearby": "Procurar imagens próximas…", - "confirm": "A imagem selecionada mostra {title()}", - "hasMatchingPicture": "Será que uma imagem corresponde ao objeto? Selecione abaixo", - "loadMore": "Carregar mais imagens", - "loading": "A carregar imagens próximas…", - "noImageSelected": "Selecione uma imagem para ligá-la ao objeto", - "nothingFound": "Não foram encontradas imagens próximas…", - "onlyTowards": "Mostrar apenas fotografias que são tiradas em direção a este objeto", - "removeFilters": "Clique aqui para remover os filtros", - "title": "Fotografias próximas", - "withinRadius": "Mostrar apenas fotografias que são tiradas num raio de {radius} metros deste objeto" - }, "pleaseLogin": "Entre na sua conta para adicionar uma imagem", "respectPrivacy": "Não fotografe pessoas nem placas de veículos. Não envie imagens do Google Maps, do Google Streetview ou outras fontes protegidas por direitos de autor.", "toBig": "A sua imagem é muito grande porque tem {actual_size}. Use imagens com o máximo {max_size}", diff --git a/langs/pt_BR.json b/langs/pt_BR.json index c72ba63eb6..d6af7cb4de 100644 --- a/langs/pt_BR.json +++ b/langs/pt_BR.json @@ -141,9 +141,6 @@ "doDelete": "Remover imagem", "dontDelete": "Cancelar", "isDeleted": "Excluída", - "nearbyPictures": { - "browseNearby": "Navegue pelas imagens próximas…" - }, "pleaseLogin": "Faça login para adicionar uma imagem", "respectPrivacy": "Não fotografe pessoas e nem placas de veículos. Não faça upload do Google Maps, Google Streetview ou outras fontes protegidas por direitos autorais.", "uploadDone": "Sua foto foi adicionada. Obrigado por ajudar!", diff --git a/langs/sl.json b/langs/sl.json index 70b3d22f65..ffaadefb24 100644 --- a/langs/sl.json +++ b/langs/sl.json @@ -66,8 +66,7 @@ "addPicture": "Dodaj sliko", "doDelete": "Odstrani sliko", "dontDelete": "Prekliči", - "isDeleted": "Izbrisana", - "nearbyPictures": {} + "isDeleted": "Izbrisana" }, "importInspector": {}, "importLayer": {}, diff --git a/langs/sv.json b/langs/sv.json index 06a1d189dc..35a494b8a2 100644 --- a/langs/sv.json +++ b/langs/sv.json @@ -69,7 +69,6 @@ "doDelete": "Ta bort bild", "dontDelete": "Avbryt", "isDeleted": "Borttagen", - "nearbyPictures": {}, "pleaseLogin": "Logga in för att lägga till en bild", "respectPrivacy": "Fotografera inte personer eller registreringsskyltar. Ladda inte upp från Google Maps, Google Streetview eller andra upphovsrättsskyddade källor.", "toBig": "Din bild är för stor då den är {actual_size}. Vänligen använd endast bilder som är högst {max_size}", diff --git a/langs/themes/de.json b/langs/themes/de.json index 34b663c99d..e64d44abeb 100644 --- a/langs/themes/de.json +++ b/langs/themes/de.json @@ -609,93 +609,6 @@ }, "elongated_coin": { "description": "Finde Münzpresse um deine eigenen Prägemünzen zu Pressen.", - "layers": { - "0": { - "description": "Ebene mit Münzpressen.", - "name": "Münzpressen", - "presets": { - "0": { - "title": "Eine Münzpresse" - } - }, - "tagRenderings": { - "charge": { - "freeform": { - "placeholder": "Einwurf (z.B. 0,5€)" - }, - "mappings": { - "0": { - "then": "Eine Münze zu Pressen kostet 1 Euro." - }, - "1": { - "then": "Eine Münze zu Pressen kostet 2€." - } - }, - "question": "Wieviel kostet es eine Münze zu Pressen?", - "render": "Es kostet {charge}€ um eine Münze zu Pressen." - }, - "coin": { - "freeform": { - "placeholder": "Münzenart (z.B. 10 Cent)" - }, - "mappings": { - "0": { - "then": "Die Münzpresse benötigt eine 2 Cent Münze um zu Pressen." - }, - "1": { - "then": "Die Münzpresse benötigt eine 5 Cent Münze um zu Pressen." - }, - "2": { - "then": "Die Münzpresse benötigt eine 10 Cent Münze um zu Pressen." - }, - "3": { - "then": "Die Münzpresse benötigt eine 25 Cent Münze um zu Pressen." - }, - "4": { - "then": "Die Münzpresse benötigt eine 50 Cent Münze um zu Pressen." - } - }, - "question": "Welche Münze wird zum Pressen verwendet?", - "render": "Die Münzpresse benötigt eine {coin:type} Münze um zu Pressen." - }, - "designs": { - "freeform": { - "placeholder": "Motivanzahl (z.B. 5)" - }, - "mappings": { - "0": { - "then": "Die Münzpresse hat ein Motiv zur Auswahl." - }, - "1": { - "then": "Die Münzpresse hat zwei Motive zur Auswahl." - }, - "2": { - "then": "Die Münzpresse hat drei Motive zur Auswahl." - }, - "3": { - "then": "Die Münzpresse hat vier Motive zur Auswahl." - } - }, - "question": "Wieviele Motive sind verfügbar?", - "render": "Die Münzpresse hat {coin:design_count} Motive zur Auswahl." - }, - "indoor": { - "mappings": { - "0": { - "then": "Die Münzpresse befindet sich im Inneren." - }, - "1": { - "then": "Die Münzpresse befindet sich Draußen." - } - }, - "question": "Befindet sich die Münzpresse im Inneren?" - } - }, - "title": { - "render": "Münzpresse" - } - } - }, "title": "Münzpressen" }, "etymology": { @@ -1023,7 +936,7 @@ }, "theme-id": { "question": "Welches Thema wurde für diese Änderung verwendet?", - "render": "Geändert mit Thema {theme}" + "render": "Geändert mit Thema {theme}" }, "version": { "question": "Mit welcher Version von MapComplete wurde diese Änderung gemacht?", diff --git a/langs/themes/en.json b/langs/themes/en.json index 1f3d9c7628..d07b53164f 100644 --- a/langs/themes/en.json +++ b/langs/themes/en.json @@ -438,7 +438,7 @@ "then": "This shop does not repair climbing shoes" } }, - "question": "Does this shoe repair shop also repair clibming shoes?" + "question": "Does this shoe repair shop also repair climbing shoes?" } }, "=presets": { @@ -609,93 +609,6 @@ }, "elongated_coin": { "description": "Find penny presses to create your own elongated coins.", - "layers": { - "0": { - "description": "Layer showing penny presses.", - "name": "Penny Presses", - "presets": { - "0": { - "title": "a penny press" - } - }, - "tagRenderings": { - "charge": { - "freeform": { - "placeholder": "Cost (e.g. 0.50 EUR)" - }, - "mappings": { - "0": { - "then": "It costs 1 euro to press a penny." - }, - "1": { - "then": "It costs 2 euros to press a penny." - } - }, - "question": "How much does it cost to press a penny?", - "render": "It costs {charge} to press a penny." - }, - "coin": { - "freeform": { - "placeholder": "Coin type (e.g. 10cent)" - }, - "mappings": { - "0": { - "then": "This penny press uses a 2 cent coin for pressing." - }, - "1": { - "then": "This penny press uses a 5 cent coin for pressing." - }, - "2": { - "then": "This penny press uses a 10 cent coin for pressing." - }, - "3": { - "then": "This penny press uses a 25 cent coin for pressing." - }, - "4": { - "then": "This penny press uses a 50 cent coin for pressing." - } - }, - "question": "What coin is used for pressing?", - "render": "This penny press uses a {coin:type} coin for pressing." - }, - "designs": { - "freeform": { - "placeholder": "Number of designs (e.g. 5)" - }, - "mappings": { - "0": { - "then": "This penny press has one design available." - }, - "1": { - "then": "This penny press has two designs available." - }, - "2": { - "then": "This penny press has three designs available." - }, - "3": { - "then": "This penny press has four designs available." - } - }, - "question": "How many designs are available?", - "render": "This penny press has {coin:design_count} designs available." - }, - "indoor": { - "mappings": { - "0": { - "then": "This penny press is located indoors." - }, - "1": { - "then": "This penny press is located outdoors." - } - }, - "question": "Is the penny press indoors?" - } - }, - "title": { - "render": "Penny Press" - } - } - }, "title": "Penny Presses" }, "etymology": { @@ -1023,7 +936,7 @@ }, "theme-id": { "question": "What theme was used to make this change?", - "render": "Change with theme {theme}" + "render": "Change with theme {theme}" }, "version": { "question": "What version of MapComplete was used to make this change?", diff --git a/langs/themes/es.json b/langs/themes/es.json index 759a8a2097..844eb6904c 100644 --- a/langs/themes/es.json +++ b/langs/themes/es.json @@ -14,6 +14,31 @@ }, "atm": { "description": "Este mapa muestra los cajeros automáticos para retirar o ingresar dinero", + "layers": { + "3": { + "override": { + "=tagRenderings": { + "0": { + "render": { + "special": { + "text": "Importar este ATM" + } + } + }, + "1": { + "render": "OpenStreetMap sabe sobre un ATM que es {_closest_osm_poi_distance} de distancia. " + }, + "2": { + "render": { + "special": { + "message": "Añade todas las etiquetas sugieridas al ATM más cercano" + } + } + } + } + } + } + }, "title": "Cajeros automáticos" }, "bag": { @@ -91,7 +116,7 @@ }, "bookcases": { "description": "Una librería pública es un pequeño armario en la calle, una caja, una vieja cabina telefónica o algún otro objeto donde se guardan libros. Todo el mundo puede colocar o coger un libro. Este mapa pretende recoger todas estas librerías.", - "title": "Mapa abierto de estanterías" + "title": "Estanterías publicas" }, "cafes_and_pubs": { "description": "Cafés, pubs y bares", @@ -383,9 +408,48 @@ } } } + }, + "1": { + "override": { + "+tagRenderings": { + "0": { + "mappings": { + "0": { + "then": "Esta tienda repara zapatos de escalada" + }, + "1": { + "then": "Esta tienda no repara zapatos de escalada" + } + }, + "question": "¿Esta tienda de reparación de zapatos repara zapatos de escalada?" + } + } + } + }, + "2": { + "override": { + "+tagRenderings": { + "0": { + "mappings": { + "0": { + "then": "Esta tienda repara zapatos de escalada" + }, + "1": { + "then": "Esta tienda no repara zapatos de escalada" + } + }, + "question": "¿Esta tienda de reparación de zapatos también repara zapatos de escalada?" + } + }, + "=presets": { + "0": { + "title": "una tienda de reparación de zapatos" + } + } + } } }, - "title": "Mapa Abierto de Escalada" + "title": "Gimnasios de escalada, clubes y lugares" }, "clock": { "description": "Mapa con todos los relojes públicos", @@ -533,7 +597,7 @@ }, "cyclofix": { "description": "El objetivo de este mapa es presentar a los ciclistas con una solución fácil de utilizar para encontrar la infraestructura apropiada para sus necesidades.

Puedes seguir tu localización precisa (móvil solo) y seleccionar las capas que son relevantes para ti en la esquina inferior izquierda. Tgambién puedes utilizar esta herramienta para editar o añadir pines (puntos de interés) al mapa y proveer más datos respondiendo a preguntas.

Todos los cambios que hagas se guardarán de manera automática en la base de datos global de OpenStreetMap y podrán ser utilizados libremente por otros.

Para más información sobre el proyecto cyclofix, ve a cyclofix.osm.be.", - "title": "Cyclofix - un mapa abierto para ciclistas" + "title": "Cyclofix - un mapa para ciclistas" }, "drinking_water": { "description": "En este mapa, se muestran los puntos de agua potable accesibles públicamente y pueden añadirse fácilmente", @@ -543,6 +607,10 @@ "description": "En este mapa, encontrará información sobre todos los tipos de escuelas y centros de educación y puede añadir fácilmente más información", "title": "Educación" }, + "elongated_coin": { + "description": "Encuentra prensas de centavo para crear tus propias monedas alargadas.", + "title": "Prensa de centavo" + }, "etymology": { "description": "En este mapa, puedes ver el nombre de un objeto. Las calles, edificios, ... provienen de OpenStreetMap que tienen enlace con Wikidata. En la ventana emergente, verás el artículo de Wikipedia (si existe) o un recuadro de wikidata del nombre del objeto. Si el objeto en sí tiene una página wikipedia, también se mostrará.

¡Puedes contribuir! Acerca el zoom lo suficiente y aparecerán todas las calles. Puedes hacer clic en una y aparecerá un cuadro de búsqueda en Wikidata. Con unos pocos clics, puedes añadir un enlace etimológico. Ten en cuenta que para ello necesitas una cuenta gratuita de OpenStreetMap.", "layers": { @@ -583,7 +651,7 @@ } }, "shortDescription": "¿Cual es el origen de un topónimo?", - "title": "Mapa Abierto Etimológico" + "title": "Etimología - a qué se debe el nombre de una calle?" }, "facadegardens": { "description": "Los jardines de fachada, las fachadas verdes y los árboles en la ciudad no sólo aportan paz y tranquilidad, sino también una ciudad más bella, una mayor biodiversidad, un efecto refrescante y una mejor calidad del aire.
Klimaan VZW y Mechelen Klimaatneutraal quieren trazar un mapa de los jardines de fachada existentes y nuevos como ejemplo para las personas que quieran construir su propio jardín o para los paseantes urbanos amantes de la naturaleza.
Más información sobre el proyecto en klimaan.be.", @@ -689,7 +757,7 @@ }, "ghostbikes": { "description": "Una bicicleta fantasma es un monumento en memoria de un ciclista fallecido en un accidente de tráfico, en forma de una bicicleta blanca colocada permanentemente cerca del lugar del accidente.

En este mapa se pueden ver todas las bicicletas fantasma conocidas por OpenStreetMap. ¿Falta alguna bicicleta fantasma? Todo el mundo puede añadir o actualizar información aquí - sólo necesitas tener una cuenta (gratuita) de OpenStreetMap.

Existe una cuenta automatizada en Mastodon que publica un resumen mensual de las bicis fantasma de todo el mundo

", - "title": "Bicicleta blanca" + "title": "Bicicletas blanca" }, "grb": { "description": "Este tema es un intento de automatizar la importación GRB.", diff --git a/langs/themes/fr.json b/langs/themes/fr.json index 393f2d7a21..21da83331b 100644 --- a/langs/themes/fr.json +++ b/langs/themes/fr.json @@ -604,19 +604,6 @@ "description": "Sur cette carte, vous trouverez des informations concernant tous les types d'écoles et d'enseignement. Vous pouvez facilement ajouter plus d'informations", "title": "Enseignement" }, - "elongated_coin": { - "layers": { - "0": { - "tagRenderings": { - "charge": { - "freeform": { - "placeholder": "Coût (par ex. 0.50 EUR)" - } - } - } - } - } - }, "etymology": { "description": "Retrouvez sur cette carte l’origine du nom d’un élément. Les rues, bâtiments, etc. proviennent d’OpenStreetMap et sont liés aux données venant de Wikidata. La fenêtre pop-up affiche l’article Wikipedia (s’il existe) ou l’infobox Wikidata de l’objet dont provient le nom. Si l’objet a sa propre page Wikipedia, elle sera aussi affichée.

Vous pouvez contribuer aussi ! Zoomez suffisamment et toutes les rues seront affichées. Cliquez sur l'une d'elles et une boîte de recherche Wikidata apparaîtra. En quelques clics, vous pouvez ajouter un lien étymologique. Vous devez disposer d’un compte OpenStreetMap gratuit.", "layers": { @@ -893,7 +880,7 @@ }, "theme-id": { "question": "Quel thème a été utilisé pour faire cette modification ?", - "render": "Modifié avec le thème {theme}" + "render": "Modifié avec le thème {theme}" }, "version": { "question": "Quelle version de MapComplete a été utilisée pour faire cette modification ?", diff --git a/langs/zh_Hant.json b/langs/zh_Hant.json index e168f70f34..8772a0778d 100644 --- a/langs/zh_Hant.json +++ b/langs/zh_Hant.json @@ -280,20 +280,6 @@ "doDelete": "移除圖片", "dontDelete": "取消", "isDeleted": "已移除", - "nearbyPictures": { - "allFiltered": "沒有符合篩選器的圖片", - "browseNearby": "瀏覽附近的圖片…", - "confirm": "選取的圖片顯示 {title()}", - "hasMatchingPicture": "有圖片符合物件嗎?請從底下結果選取", - "loadMore": "載入更多圖片", - "loading": "載入附近圖片…", - "noImageSelected": "選取圖片來連結到物件", - "nothingFound": "附近沒有找到圖片…", - "onlyTowards": "只顯示往這個物件方向照的圖片", - "removeFilters": "點這裡來移除篩選", - "title": "附近圖片", - "withinRadius": "只顯示這個物件半徑 {radius} 公尺內照的圖片" - }, "pleaseLogin": "請登入以新增圖片", "respectPrivacy": "請別照人像或是車牌,不要上傳 Google 地圖、Google 街景或其他受版權保護的資料來源。", "toBig": "{actual_size} 因此照片太大,請使用最大 {max_size} 的照片", diff --git a/package-lock.json b/package-lock.json index f977c46026..dc8d0f6370 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mapcomplete", - "version": "0.31.1", + "version": "0.31.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mapcomplete", - "version": "0.31.1", + "version": "0.31.4", "license": "GPL-3.0-or-later", "dependencies": { "@rgossiaux/svelte-headlessui": "^1.0.2", @@ -40,7 +40,7 @@ "mangrove-reviews-typescript": "^1.1.0", "maplibre-gl": "^3.2.0", "opening_hours": "^3.6.0", - "osm-auth": "^1.0.2", + "osm-auth": "^2.2.0", "osmtogeojson": "^3.0.0-beta.5", "papaparse": "^5.3.1", "pic4carto": "^2.1.15", @@ -72,9 +72,13 @@ "@types/prompt-sync": "^4.1.0", "@types/wikidata-sdk": "^6.1.0", "@types/xml2js": "^0.4.9", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", "assert": "^2.0.0", "chai": "^4.3.6", "dependency-cruiser": "^10.4.0", + "eslint": "^8.45.0", + "eslint-plugin-svelte": "^2.32.2", "fs": "0.0.1-security", "node-html-parser": "^6.1.5", "prettier": "^2.8.8", @@ -89,12 +93,19 @@ "ts-node": "^10.9.1", "ts2json-schema": "^1.4.0", "tslib": "^2.5.0", - "tslint": "^6.1.3", - "tslint-no-circular-imports": "^0.7.0", "typescript": "^4.7.4", "vite": "^4.0.5" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -1634,6 +1645,144 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "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.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "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" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "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" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "node_modules/@jest/schemas": { "version": "29.4.0", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.0.tgz", @@ -3682,9 +3831,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "node_modules/@types/json5": { @@ -3766,6 +3915,12 @@ "@types/node": "*" } }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, "node_modules/@types/showdown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz", @@ -3803,6 +3958,295 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.1.0.tgz", + "integrity": "sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.1.0", + "@typescript-eslint/type-utils": "6.1.0", + "@typescript-eslint/utils": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/eslint-plugin/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.1.0.tgz", + "integrity": "sha512-hIzCPvX4vDs4qL07SYzyomamcs2/tQYXg5DtdAfj35AyJ5PIUqhsLf4YrEIFzZcND7R2E8tpQIZKayxg8/6Wbw==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.1.0", + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/typescript-estree": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.1.0.tgz", + "integrity": "sha512-AxjgxDn27hgPpe2rQe19k0tXw84YCOsjDJ2r61cIebq1t+AIxbgiXKvD4999Wk49GVaAcdJ/d49FYel+Pp3jjw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.1.0.tgz", + "integrity": "sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.1.0", + "@typescript-eslint/utils": "6.1.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.1.0.tgz", + "integrity": "sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.1.0.tgz", + "integrity": "sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.1.0.tgz", + "integrity": "sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.1.0", + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/typescript-estree": "6.1.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/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/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils/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/@typescript-eslint/visitor-keys": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz", + "integrity": "sha512-yQeh+EXhquh119Eis4k0kYhj9vmFzNpbhM3LftWQVwqVjipCkwHBQOZutcYW+JVkjtTG9k8nrZU1UoNedPDd1A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.1.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -4107,6 +4551,15 @@ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==" }, + "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/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -4436,15 +4889,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "node_modules/builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/bytewise": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", @@ -4511,9 +4955,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001517", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", - "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", + "version": "1.0.30001525", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz", + "integrity": "sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q==", "dev": true, "funding": [ { @@ -4919,6 +5363,20 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "devOptional": true }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/css-line-break": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", @@ -5433,6 +5891,18 @@ "node": ">=0.3.1" } }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -5454,6 +5924,18 @@ "doctest-ts-improved": "dist/main.js" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -5719,6 +6201,378 @@ "source-map": "~0.6.1" } }, + "node_modules/eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", + "@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", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.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", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-svelte": { + "version": "2.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.32.2.tgz", + "integrity": "sha512-Jgbop2fNZsoxxkklZAIbDNhwAPynvnCtUXLsEC6O2qax7N/pfe2cNqT0ZoBbubXKJitQQDEyVDQ1rZs4ZWcrTA==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@jridgewell/sourcemap-codec": "^1.4.14", + "debug": "^4.3.1", + "esutils": "^2.0.3", + "known-css-properties": "^0.27.0", + "postcss": "^8.4.5", + "postcss-load-config": "^3.1.4", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.11", + "semver": "^7.5.3", + "svelte-eslint-parser": "^0.32.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0-0", + "svelte": "^3.37.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-svelte/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/eslint-plugin-svelte/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-plugin-svelte/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/eslint-scope": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", + "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/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/eslint/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/eslint/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/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/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "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" + }, + "engines": { + "node": ">=8" + }, + "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_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/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/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", @@ -5731,12 +6585,35 @@ "node": ">=4" } }, + "node_modules/esquery": { + "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" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "optional": true, - "peer": true, + "devOptional": true, "engines": { "node": ">=4.0" } @@ -6001,6 +6878,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -6035,6 +6924,40 @@ "flat": "cli.js" } }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flat-cache/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/flatted": { + "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.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -6362,6 +7285,26 @@ "node": ">=4" } }, + "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/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -6379,6 +7322,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -6720,6 +7669,15 @@ } ] }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/immutable": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.2.tgz", @@ -6742,6 +7700,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/incremental-convex-hull": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz", @@ -7012,6 +7979,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -7300,17 +8276,6 @@ "node": ">=4" } }, - "node_modules/jshashes": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/jshashes/-/jshashes-1.0.8.tgz", - "integrity": "sha512-btmQZ/w1rj8Lb6nEwvhjM7nBYoj54yaEFo2PWh3RkxZ8qNwuvOxvQYN/JxVuwoMmdIluL+XwYVJ+pEEZoSYybQ==", - "bin": { - "hashes": "bin/hashes" - }, - "engines": { - "node": "*" - } - }, "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -7334,6 +8299,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "node_modules/json-stringify-pretty-compact": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", @@ -7477,6 +8448,12 @@ "node": ">=6" } }, + "node_modules/known-css-properties": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", + "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", + "dev": true + }, "node_modules/latlon2country": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/latlon2country/-/latlon2country-1.2.6.tgz", @@ -7549,6 +8526,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -8043,6 +9026,18 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -8189,18 +9184,6 @@ "node": ">= 0.4" } }, - "node_modules/ohauth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ohauth/-/ohauth-1.0.1.tgz", - "integrity": "sha512-R9ZUN3+FVCwzeOOHCJpzA9jw/byRxp5O9X06mTL6Sp/LIQn/rLrMv6cwYctX+hoIKzRUsalGJXZ1kG5wBmSskQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dependencies": { - "jshashes": "~1.0.8" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -8372,16 +9355,14 @@ } }, "node_modules/osm-auth": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-1.1.2.tgz", - "integrity": "sha512-oLaU+c/TP7eKAZpBN4S1mv/N94IXp5A+wLpDfAVlpq/b6iikas8ZthXPqhM8QKg/qB8RaKvZPJgxqYS+5m8G8g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-2.2.0.tgz", + "integrity": "sha512-x93jAMaYWqPgfVeOMydFLFpFC8ERnlIKXwiUOrYYWTDEWqq15K/BI5UAjzuYXvLg0WxVxM8YC4N1T30SZeKJBQ==", "dependencies": { - "ohauth": "~1.0.1", - "resolve-url": "~0.2.1", "store": "~2.0.12" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/osm-polygon-features": { @@ -8488,11 +9469,29 @@ "node": ">=0.10.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", @@ -8585,6 +9584,99 @@ "splaytree": "^3.1.0" } }, + "node_modules/postcss": { + "version": "8.4.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", + "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + } + ], + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.19" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", @@ -8597,6 +9689,23 @@ "node": ">=4" } }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/potpack": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/potpack/-/potpack-2.0.0.tgz", @@ -9191,12 +10300,6 @@ "protocol-buffers-schema": "^3.3.1" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated" - }, "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -9608,6 +10711,27 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/showdown": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", @@ -9721,6 +10845,15 @@ "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -9869,12 +11002,6 @@ "node": ">=0.10.0" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "node_modules/sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -10054,9 +11181,9 @@ } }, "node_modules/svelte": { - "version": "3.55.1", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", - "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==", + "version": "3.59.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz", + "integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==", "engines": { "node": ">= 8" } @@ -10083,6 +11210,33 @@ "svelte": "^3.55.0" } }, + "node_modules/svelte-eslint-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.32.1.tgz", + "integrity": "sha512-GCSfeIzdgk53CaOzK+s/+l2igfTno3mWGkwoDYAwPes/rD9Al2fc7ksfopjx5UL87S7dw1eL73F6wNYiiuhzIA==", + "dev": true, + "dependencies": { + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "postcss": "^8.4.25", + "postcss-scss": "^4.0.6" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "svelte": "^3.37.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "svelte": { + "optional": true + } + } + }, "node_modules/svelte-hmr": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz", @@ -10242,40 +11396,6 @@ "node": ">=10.13.0" } }, - "node_modules/tailwindcss/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/tailwindcss/node_modules/postcss": { - "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==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/tailwindcss/node_modules/postcss-import": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", @@ -10310,34 +11430,6 @@ "postcss": "^8.3.3" } }, - "node_modules/tailwindcss/node_modules/postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "dependencies": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, "node_modules/tailwindcss/node_modules/postcss-nested": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", @@ -10439,6 +11531,12 @@ "utrie": "^1.0.2" } }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -10588,6 +11686,18 @@ "node": ">=14" } }, + "node_modules/ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-json-schema-generator": { "version": "0.95.0", "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-0.95.0.tgz", @@ -10809,117 +11919,6 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "devOptional": true }, - "node_modules/tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "deprecated": "TSLint has been deprecated in favor of ESLint. Please see https://github.com/palantir/tslint/issues/4534 for more information.", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "bin": { - "tslint": "bin/tslint" - }, - "engines": { - "node": ">=4.8.0" - }, - "peerDependencies": { - "typescript": ">=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >=3.0.0-dev || >= 3.1.0-dev || >= 3.2.0-dev || >= 4.0.0-dev" - } - }, - "node_modules/tslint-no-circular-imports": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", - "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", - "dev": true, - "peerDependencies": { - "tslint": ">=5.0.0", - "typescript": ">=2.1.0" - } - }, - "node_modules/tslint/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/tslint/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/tslint/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tslint/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/tslint/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/tslint/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "peerDependencies": { - "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -11519,9 +12518,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -11896,40 +12895,6 @@ "url": "https://github.com/sponsors/antfu" } }, - "node_modules/vite/node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/vite/node_modules/postcss": { - "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==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/vitefu": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.4.tgz", @@ -12404,6 +13369,12 @@ } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -13487,6 +14458,102 @@ "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==", "optional": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "globals": { + "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" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@eslint/js": { + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "dev": true + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@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": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@jest/schemas": { "version": "29.4.0", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.0.tgz", @@ -15090,9 +16157,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "dev": true }, "@types/json5": { @@ -15174,6 +16241,12 @@ "@types/node": "*" } }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, "@types/showdown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz", @@ -15210,6 +16283,185 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" }, + "@typescript-eslint/eslint-plugin": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.1.0.tgz", + "integrity": "sha512-qg7Bm5TyP/I7iilGyp6DRqqkt8na00lI6HbjWZObgk3FFSzH5ypRwAHXJhJkwiRtTcfn+xYQIMOR5kJgpo6upw==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.1.0", + "@typescript-eslint/type-utils": "6.1.0", + "@typescript-eslint/utils": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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 + } + } + }, + "@typescript-eslint/parser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.1.0.tgz", + "integrity": "sha512-hIzCPvX4vDs4qL07SYzyomamcs2/tQYXg5DtdAfj35AyJ5PIUqhsLf4YrEIFzZcND7R2E8tpQIZKayxg8/6Wbw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "6.1.0", + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/typescript-estree": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.1.0.tgz", + "integrity": "sha512-AxjgxDn27hgPpe2rQe19k0tXw84YCOsjDJ2r61cIebq1t+AIxbgiXKvD4999Wk49GVaAcdJ/d49FYel+Pp3jjw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.1.0.tgz", + "integrity": "sha512-kFXBx6QWS1ZZ5Ni89TyT1X9Ag6RXVIVhqDs0vZE/jUeWlBv/ixq2diua6G7ece6+fXw3TvNRxP77/5mOMusx2w==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.1.0", + "@typescript-eslint/utils": "6.1.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + } + }, + "@typescript-eslint/types": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.1.0.tgz", + "integrity": "sha512-+Gfd5NHCpDoHDOaU/yIF3WWRI2PcBRKKpP91ZcVbL0t5tQpqYWBs3z/GGhvU+EV1D0262g9XCnyqQh19prU0JQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.1.0.tgz", + "integrity": "sha512-nUKAPWOaP/tQjU1IQw9sOPCDavs/iU5iYLiY/6u7gxS7oKQoi4aUxXS1nrrVGTyBBaGesjkcwwHkbkiD5eBvcg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/visitor-keys": "6.1.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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 + } + } + }, + "@typescript-eslint/utils": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.1.0.tgz", + "integrity": "sha512-wp652EogZlKmQoMS5hAvWqRKplXvkuOnNzZSE0PVvsKjpexd/XznRVHAtrfHFYmqaJz0DFkjlDsGYC9OXw+OhQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.1.0", + "@typescript-eslint/types": "6.1.0", + "@typescript-eslint/typescript-estree": "6.1.0", + "semver": "^7.5.4" + }, + "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.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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 + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.1.0.tgz", + "integrity": "sha512-yQeh+EXhquh119Eis4k0kYhj9vmFzNpbhM3LftWQVwqVjipCkwHBQOZutcYW+JVkjtTG9k8nrZU1UoNedPDd1A==", + "dev": true, + "requires": { + "@typescript-eslint/types": "6.1.0", + "eslint-visitor-keys": "^3.4.1" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -15448,6 +16700,12 @@ "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", "integrity": "sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA==" }, + "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 + }, "asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -15682,12 +16940,6 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha512-wxXCdllwGhI2kCC0MnvTGYTMvnVZTvqgypkiTI8Pa5tcz2i6VqsqwYGgqwXji+4RgCzms6EajE4IxiUH6HH8nQ==", - "dev": true - }, "bytewise": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/bytewise/-/bytewise-1.1.0.tgz", @@ -15736,9 +16988,9 @@ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, "caniuse-lite": { - "version": "1.0.30001517", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", - "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", + "version": "1.0.30001525", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001525.tgz", + "integrity": "sha512-/3z+wB4icFt3r0USMwxujAqRvaD/B7rvGTsKhbhSQErVrJvkZCLhgNLJxU8MevahQVH6hCU9FsHdNUFbiwmE7Q==", "dev": true }, "canvg": { @@ -16045,6 +17297,17 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "devOptional": true }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, "css-line-break": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", @@ -16414,6 +17677,15 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, "dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", @@ -16432,6 +17704,15 @@ "typescript": "^4.6.2" } }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -16647,17 +17928,297 @@ "source-map": "~0.6.1" } }, + "eslint": { + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", + "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", + "@humanwhocodes/config-array": "^0.11.10", + "@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", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.6.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", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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 + }, + "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 + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "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" + } + }, + "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 + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "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" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-plugin-svelte": { + "version": "2.32.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-svelte/-/eslint-plugin-svelte-2.32.2.tgz", + "integrity": "sha512-Jgbop2fNZsoxxkklZAIbDNhwAPynvnCtUXLsEC6O2qax7N/pfe2cNqT0ZoBbubXKJitQQDEyVDQ1rZs4ZWcrTA==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@jridgewell/sourcemap-codec": "^1.4.14", + "debug": "^4.3.1", + "esutils": "^2.0.3", + "known-css-properties": "^0.27.0", + "postcss": "^8.4.5", + "postcss-load-config": "^3.1.4", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.11", + "semver": "^7.5.3", + "svelte-eslint-parser": "^0.32.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" + } + }, + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "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 + } + } + }, + "eslint-scope": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", + "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "requires": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "dependencies": { + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, + "esquery": { + "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" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + } + }, "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "optional": true, - "peer": true + "devOptional": true }, "estree-walker": { "version": "2.0.2", @@ -16873,6 +18434,15 @@ "escape-string-regexp": "^1.0.5" } }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -16895,6 +18465,33 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "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" + } + } + } + }, + "flatted": { + "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.15.2", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", @@ -17149,6 +18746,20 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true }, + "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" + } + }, "gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -17163,6 +18774,12 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", @@ -17411,6 +19028,12 @@ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, "immutable": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.2.tgz", @@ -17427,6 +19050,12 @@ "resolve-from": "^4.0.0" } }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, "incremental-convex-hull": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/incremental-convex-hull/-/incremental-convex-hull-1.0.1.tgz", @@ -17621,6 +19250,12 @@ "define-properties": "^1.1.3" } }, + "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 + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -17830,11 +19465,6 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, - "jshashes": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/jshashes/-/jshashes-1.0.8.tgz", - "integrity": "sha512-btmQZ/w1rj8Lb6nEwvhjM7nBYoj54yaEFo2PWh3RkxZ8qNwuvOxvQYN/JxVuwoMmdIluL+XwYVJ+pEEZoSYybQ==" - }, "json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", @@ -17855,6 +19485,12 @@ "jsonify": "^0.0.1" } }, + "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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, "json-stringify-pretty-compact": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", @@ -17968,6 +19604,12 @@ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "dev": true }, + "known-css-properties": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.27.0.tgz", + "integrity": "sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==", + "dev": true + }, "latlon2country": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/latlon2country/-/latlon2country-1.2.6.tgz", @@ -18022,6 +19664,12 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -18401,6 +20049,18 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "dev": true }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -18513,14 +20173,6 @@ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, - "ohauth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ohauth/-/ohauth-1.0.1.tgz", - "integrity": "sha512-R9ZUN3+FVCwzeOOHCJpzA9jw/byRxp5O9X06mTL6Sp/LIQn/rLrMv6cwYctX+hoIKzRUsalGJXZ1kG5wBmSskQ==", - "requires": { - "jshashes": "~1.0.8" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -18651,12 +20303,10 @@ "dev": true }, "osm-auth": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-1.1.2.tgz", - "integrity": "sha512-oLaU+c/TP7eKAZpBN4S1mv/N94IXp5A+wLpDfAVlpq/b6iikas8ZthXPqhM8QKg/qB8RaKvZPJgxqYS+5m8G8g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-2.2.0.tgz", + "integrity": "sha512-x93jAMaYWqPgfVeOMydFLFpFC8ERnlIKXwiUOrYYWTDEWqq15K/BI5UAjzuYXvLg0WxVxM8YC4N1T30SZeKJBQ==", "requires": { - "ohauth": "~1.0.1", - "resolve-url": "~0.2.1", "store": "~2.0.12" } }, @@ -18732,11 +20382,23 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, "pathe": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.0.tgz", @@ -18816,6 +20478,46 @@ "splaytree": "^3.1.0" } }, + "postcss": { + "version": "8.4.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.26.tgz", + "integrity": "sha512-jrXHFF8iTloAenySjM/ob3gSj7pCu0Ji49hnjqzsgSRa50hkWCKD0HQ+gMNJkW38jBI68MpAAg7ZWwHwX8NMMw==", + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "dependencies": { + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" + } + } + }, + "postcss-load-config": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", + "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^1.10.2" + } + }, + "postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "requires": {} + }, + "postcss-scss": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.6.tgz", + "integrity": "sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==", + "dev": true, + "requires": {} + }, "postcss-selector-parser": { "version": "6.0.11", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", @@ -19236,11 +20938,6 @@ "protocol-buffers-schema": "^3.3.1" } }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==" - }, "restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", @@ -19553,6 +21250,21 @@ } } }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, "showdown": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/showdown/-/showdown-2.1.0.tgz", @@ -19632,6 +21344,12 @@ "resolved": "https://registry.npmjs.org/skmeans/-/skmeans-0.9.7.tgz", "integrity": "sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg==" }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", @@ -19739,12 +21457,6 @@ } } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "sshpk": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", @@ -19878,9 +21590,9 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" }, "svelte": { - "version": "3.55.1", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.55.1.tgz", - "integrity": "sha512-S+87/P0Ve67HxKkEV23iCdAh/SX1xiSfjF1HOglno/YTbSTW7RniICMCofWGdJJbdjw3S+0PfFb1JtGfTXE0oQ==" + "version": "3.59.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-3.59.2.tgz", + "integrity": "sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==" }, "svelte-check": { "version": "3.0.3", @@ -19898,6 +21610,19 @@ "typescript": "^4.9.4" } }, + "svelte-eslint-parser": { + "version": "0.32.1", + "resolved": "https://registry.npmjs.org/svelte-eslint-parser/-/svelte-eslint-parser-0.32.1.tgz", + "integrity": "sha512-GCSfeIzdgk53CaOzK+s/+l2igfTno3mWGkwoDYAwPes/rD9Al2fc7ksfopjx5UL87S7dw1eL73F6wNYiiuhzIA==", + "dev": true, + "requires": { + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.0.0", + "espree": "^9.0.0", + "postcss": "^8.4.25", + "postcss-scss": "^4.0.6" + } + }, "svelte-hmr": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.1.tgz", @@ -19983,21 +21708,6 @@ "is-glob": "^4.0.3" } }, - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "postcss": { - "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==", - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - }, "postcss-import": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", @@ -20016,15 +21726,6 @@ "camelcase-css": "^2.0.1" } }, - "postcss-load-config": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz", - "integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==", - "requires": { - "lilconfig": "^2.0.5", - "yaml": "^1.10.2" - } - }, "postcss-nested": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.0.tgz", @@ -20108,6 +21809,12 @@ "utrie": "^1.0.2" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -20231,6 +21938,13 @@ "punycode": "^2.3.0" } }, + "ts-api-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.1.tgz", + "integrity": "sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==", + "dev": true, + "requires": {} + }, "ts-json-schema-generator": { "version": "0.95.0", "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-0.95.0.tgz", @@ -20392,96 +22106,6 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "devOptional": true }, - "tslint": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", - "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.3.0", - "commander": "^2.12.1", - "diff": "^4.0.1", - "glob": "^7.1.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.3", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.13.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "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 - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tslint-no-circular-imports": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz", - "integrity": "sha512-k3wxpeMC4ef40UbpfBVHEHIzKfNZq5/SCtAO1YjGsaNTklo+K53/TWLrym+poA65RJFDiYgYNWvkeIIkJNA0Vw==", - "dev": true, - "requires": {} - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -21015,9 +22639,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "typescript": { - "version": "4.9.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", - "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" }, "typescript-json-schema": { "version": "0.50.1", @@ -21248,23 +22872,6 @@ "postcss": "^8.4.20", "resolve": "^1.22.1", "rollup": "^3.7.0" - }, - "dependencies": { - "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" - }, - "postcss": { - "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==", - "requires": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - } - } } }, "vite-node": { @@ -21605,4 +23212,4 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 4809b78332..a49a7d3d6b 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,58 @@ { "name": "mapcomplete", - "version": "0.31.3", + "version": "0.32.0", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", "homepage": "https://mapcomplete.org", "main": "index.ts", "type": "module", + "config": { + "#": "Various endpoints that are instance-specific", + "#oauth_credentials:comment": [ + "`oauth_credentials` are the OAuth-2 credentials for the production-OSM server and the test-server.", + "Are you deploying your own instance? Register your application too.", + "See https://wiki.openstreetmap.org/wiki/OAuth#Registering_your_application_as_OAuth_2.0_consumer for instructions", + "Use `https:////land.html` as redirect URIs. You can add `http://127.0.0.1:1234/land.html` too for local development.", + "Alternatively, you can override the `osm` credentials using the environment variables `VITE_OSM_OAUTH_CLIENT_ID` and `VITE_OSM_OAUTH_SECRET`" + ], + "oauth_credentials": { + "osm_pietervdvn": { + "#": "This client_id is registered by 'Pieter Vander Vennet' on OSM.org", + "oauth_client_id": "sa1ngLJBJ8McmzHElN8NYtIDm5TZTYEYhq3-0snO4Qc", + "oauth_secret": "XU_cD5Mvw9VKk9T0t_gO8V7cbRC4Hmw2Tb4Rv0Zmz-U", + "url": "https://www.openstreetmap.org" + }, + "osm": { + "#": "This client-id is registered by 'MapComplete' on osm.org", + "oauth_client_id": "K93H1d8ve7p-tVLE1ZwsQ4lAFLQk8INx5vfTLMu5DWk", + "oauth_secret": "NBWGhWDrD3QDB35xtVuxv4aExnmIt4FA_WgeLtwxasg", + "url": "https://www.openstreetmap.org" + }, + "osm-test": { + "oauth_client_id": "HwUn6GPxGm1m9WwMarxTglhy6dBTM4YkaV1I9h6pDGU", + "oauth_secret": "luFZtPJg7j96K6WM6RpcZ_3M-r6muuDq6fG1ygk0I_4", + "url": "https://master.apis.dev.openstreetmap.org" + } + }, + "api_keys": { + "#": "Various API-keys for various services. Feel free to reuse those in another MapComplete-hosted version", + "imgur": "7070e7167f0a25a", + "mapillary_v4": "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" + }, + "default_overpass_urls": [ + "https://overpass-api.de/api/interpreter", + "https://overpass.kumi.systems/api/interpreter", + "https://overpass.openstreetmap.ru/cgi/interpreter" + ], + "country_coder_host": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/latlon2country" + }, "scripts": { "start": "npm run generate:layeroverview && npm run strt", "strt": "vite --host", "strttest": "export NODE_OPTIONS=--max_old_space_size=8364 && parcel serve test.html assets/templates/*.svg assets/templates/fonts/*.ttf", "watch:css": "tailwindcss -i index.css -o public/css/index-tailwind-output.css --watch", - "generate:css": "tailwindcss -i index.css -o public/css/index-tailwind-output.css", + "generate:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css", "generate:doctests": "doctest-ts-improved . --ignore .*.spec.ts --ignore .*ConfigJson.ts", "test:run-only": "vitest --run test", "test": " export NODE_OPTIONS=\"--max-old-space-size=8192\" && npm run clean:tests && (npm run generate:doctests 2>&1 | grep -v \"No doctests found in\") && npm run test:run-only && npm run clean:tests", @@ -39,6 +79,9 @@ "generate": "mkdir -p ./assets/generated; npm run generate:licenses; npm run generate:images; npm run generate:charging-stations; npm run generate:translations; npm run reset:layeroverview; npm run generate:service-worker", "generate:charging-stations": "cd ./assets/layers/charging_station && vite-node csvToJson.ts && cd -", "prepare-deploy": "npm run generate:service-worker && ./scripts/build.sh", + "lint": "npm run lint:prettier && npm run lint:eslint", + "lint:eslint": "eslint ./src", + "lint:prettier": "prettier --check '**/*.ts' '**/*.svelte'", "format": "prettier --write '**/*.ts' '**/*.svelte'", "clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm", "clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|test\\|studio\\|theme\\|style_test\\|statistics\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)", @@ -93,7 +136,7 @@ "mangrove-reviews-typescript": "^1.1.0", "maplibre-gl": "^3.2.0", "opening_hours": "^3.6.0", - "osm-auth": "^1.0.2", + "osm-auth": "^2.2.0", "osmtogeojson": "^3.0.0-beta.5", "papaparse": "^5.3.1", "pic4carto": "^2.1.15", @@ -125,9 +168,13 @@ "@types/prompt-sync": "^4.1.0", "@types/wikidata-sdk": "^6.1.0", "@types/xml2js": "^0.4.9", + "@typescript-eslint/eslint-plugin": "^6.1.0", + "@typescript-eslint/parser": "^6.1.0", "assert": "^2.0.0", "chai": "^4.3.6", "dependency-cruiser": "^10.4.0", + "eslint": "^8.45.0", + "eslint-plugin-svelte": "^2.32.2", "fs": "0.0.1-security", "node-html-parser": "^6.1.5", "prettier": "^2.8.8", @@ -142,8 +189,6 @@ "ts-node": "^10.9.1", "ts2json-schema": "^1.4.0", "tslib": "^2.5.0", - "tslint": "^6.1.3", - "tslint-no-circular-imports": "^0.7.0", "typescript": "^4.7.4", "vite": "^4.0.5" } diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index e2b1f304ba..91a0cd7e0e 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -698,13 +698,6 @@ video { position: sticky; } -.inset-0 { - top: 0px; - right: 0px; - bottom: 0px; - left: 0px; -} - .-inset-1 { top: -0.25rem; right: -0.25rem; @@ -744,26 +737,6 @@ video { bottom: 0px; } -.top-12 { - top: 3rem; -} - -.left-3 { - left: 0.75rem; -} - -.top-3 { - top: 0.75rem; -} - -.right-2 { - right: 0.5rem; -} - -.bottom-3 { - bottom: 0.75rem; -} - .right-1\/3 { right: 33.333333%; } @@ -780,14 +753,6 @@ video { top: 2.5rem; } -.left-1\/2 { - left: 50%; -} - -.top-1\/2 { - top: 50%; -} - .isolate { isolation: isolate; } @@ -874,6 +839,11 @@ video { margin-right: 0.5rem; } +.my-1 { + margin-top: 0.25rem; + margin-bottom: 0.25rem; +} + .mx-4 { margin-left: 1rem; margin-right: 1rem; @@ -896,10 +866,6 @@ video { margin-right: 1.5rem; } -.mt-6 { - margin-top: 1.5rem; -} - .mt-1 { margin-top: 0.25rem; } @@ -976,14 +942,6 @@ video { margin-left: -1.5rem; } -.-ml-12 { - margin-left: -3rem; -} - -.-mt-12 { - margin-top: -3rem; -} - .mr-3 { margin-right: 0.75rem; } @@ -1122,11 +1080,6 @@ video { height: 1rem; } -.h-min { - height: -webkit-min-content; - height: min-content; -} - .h-1\/2 { height: 50%; } @@ -1328,23 +1281,6 @@ video { animation: spin 1s linear infinite; } -@-webkit-keyframes pulse { - 50% { - opacity: .5; - } -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -.animate-pulse { - -webkit-animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; - animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; -} - .cursor-pointer { cursor: pointer; } @@ -1458,6 +1394,12 @@ video { column-gap: 0.25rem; } +.space-x-1 > :not([hidden]) ~ :not([hidden]) { + --tw-space-x-reverse: 0; + margin-right: calc(0.25rem * var(--tw-space-x-reverse)); + margin-left: calc(0.25rem * calc(1 - var(--tw-space-x-reverse))); +} + .space-y-reverse > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 1; } @@ -1506,8 +1448,8 @@ video { overflow: hidden; } -.overflow-scroll { - overflow: scroll; +.overflow-x-auto { + overflow-x: auto; } .overflow-y-auto { @@ -1557,18 +1499,14 @@ video { border-radius: 1rem; } -.rounded-3xl { - border-radius: 1.5rem; +.rounded-md { + border-radius: 0.375rem; } .rounded-lg { border-radius: 0.5rem; } -.rounded-md { - border-radius: 0.375rem; -} - .rounded-sm { border-radius: 0.125rem; } @@ -1699,11 +1637,6 @@ video { background-color: rgb(255 255 255 / var(--tw-bg-opacity)); } -.bg-red-500 { - --tw-bg-opacity: 1; - background-color: rgb(239 68 68 / var(--tw-bg-opacity)); -} - .bg-black { --tw-bg-opacity: 1; background-color: rgb(0 0 0 / var(--tw-bg-opacity)); @@ -1740,10 +1673,6 @@ video { padding: 0.5rem; } -.p-3 { - padding: 0.75rem; -} - .p-4 { padding: 1rem; } @@ -1780,6 +1709,11 @@ video { padding-right: 0.5rem; } +.py-2 { + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + .pl-1 { padding-left: 0.25rem; } @@ -1788,10 +1722,6 @@ video { padding-right: 0.5rem; } -.pl-3 { - padding-left: 0.75rem; -} - .pl-2 { padding-left: 0.5rem; } @@ -1820,6 +1750,10 @@ video { padding-left: 1rem; } +.pl-3 { + padding-left: 0.75rem; +} + .pr-0 { padding-right: 0px; } @@ -1879,11 +1813,6 @@ video { line-height: 1.5rem; } -.text-xs { - font-size: 0.75rem; - line-height: 1rem; -} - .font-bold { font-weight: 700; } @@ -2122,12 +2051,6 @@ video { backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); } -.transition-colors { - transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; - transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - transition-duration: 150ms; -} - .transition { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, -webkit-transform, -webkit-filter, -webkit-backdrop-filter; transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; @@ -2136,6 +2059,12 @@ video { transition-duration: 150ms; } +.transition-colors { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + .ease-in-out { transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); } @@ -2736,10 +2665,6 @@ a.link-underline { } @media (min-width: 640px) { - .sm\:top-3 { - top: 0.75rem; - } - .sm\:m-2 { margin: 0.5rem; } @@ -2778,40 +2703,18 @@ a.link-underline { height: 6rem; } - .sm\:w-fit { - width: -webkit-fit-content; - width: -moz-fit-content; - width: fit-content; - } - .sm\:w-24 { width: 6rem; } - .sm\:max-w-sm { - max-width: 24rem; - } - .sm\:max-w-xl { max-width: 36rem; } - .sm\:flex-row { - flex-direction: row; - } - - .sm\:flex-wrap { - flex-wrap: wrap; - } - .sm\:flex-nowrap { flex-wrap: nowrap; } - .sm\:items-start { - align-items: flex-start; - } - .sm\:items-stretch { align-items: stretch; } @@ -2832,10 +2735,6 @@ a.link-underline { padding: 0.5rem; } - .sm\:pl-0 { - padding-left: 0px; - } - .sm\:pt-1 { padding-top: 0.25rem; } @@ -2901,10 +2800,6 @@ a.link-underline { width: 2rem; } - .md\:w-1\/3 { - width: 33.333333%; - } - .md\:w-6\/12 { width: 50%; } diff --git a/scripts/@types/eli.ts b/scripts/@types/eli.ts new file mode 100644 index 0000000000..87e1f85afd --- /dev/null +++ b/scripts/@types/eli.ts @@ -0,0 +1,213 @@ +import { Feature, FeatureCollection } from "geojson" + +export interface Eli extends FeatureCollection { + features: EliEntry[] +} + +export interface EliEntry extends Feature { + properties: { + /** + * The name of the imagery source + */ + name: string + + /** + * Whether the imagery name should be translated + */ + i18n?: boolean + + /** + * Type of layer + */ + type: "tms" | "wms" | "bing" | "scanex" | "wms_endpoint" | "wmts" + + /** + * A rough categorisation of different types of layers. + * @see https://github.com/osmlab/editor-layer-index/blob/gh-pages/CONTRIBUTING.md#categories + */ + category?: + | "photo" + | "map" + | "historicmap" + | "osmbasedmap" + | "historicphoto" + | "qa" + | "elevation" + | "other" + + /** + * A URL template for imagery tiles + */ + url: string + + /** + * The minimum zoom level + */ + min_zoom?: number + + /** + * The maximum zoom level + */ + max_zoom?: number + + /** + * explicit/implicit permission by the owner for use in OSM + */ + permission_osm?: "explicit" | "implicit" | "no" + + /** + * A URL for the license or permissions for the imagery + */ + license_url?: string + + /** + * A URL for the privacy policy of the operator or false if there is no existing privacy policy for tis imagery. + */ + privacy_policy_url?: string | boolean + + /** + * A unique identifier for the source; used in imagery_used changeset tag + */ + id: string + + /** + * A short English-language description of the source + */ + description?: string + + /** + * The ISO 3166-1 alpha-2 two letter country code in upper case. Use ZZ for unknown or multiple. + */ + country_code?: string + + /** + * Whether this imagery should be shown in the default world-wide menu + */ + default?: boolean + + /** + * Whether this imagery is the best source for the region + */ + best?: boolean + + /** + * The age of the oldest imagery or data in the source, as an RFC3339 date or leading portion of one + */ + start_date?: string + + /** + * The age of the newest imagery or data in the source, as an RFC3339 date or leading portion of one + */ + end_date?: string + + /** + * HTTP header to check for information if the tile is invalid + */ + no_tile_header?: { [header: string]: string[] } | null + + /** + * 'true' if tiles are transparent and can be overlaid on another source + */ + overlay?: boolean + + /** + * Available projections + */ + available_projections?: string[] + + /** + * Attribution + */ + attribution?: { + /** + * URL + */ + url?: string + + /** + * Text + */ + text?: string + + /** + * HTML formatted attribution + */ + html?: string + + /** + * Whether attribution is required + */ + required?: boolean + } + + /** + * A URL for an image, that can be displayed in the list of imagery layers next to the name + */ + icon?: string + + /** + * A link to an EULA text that has to be accepted by the user, before the imagery source is added. Can contain {lang} to be replaced by a current user language wiki code (like FR:) or an empty string for the default English text. + */ + eula?: string + + /** + * A URL for an image, that is displayed in the mapview for attribution + */ + "logo-image"?: string + + /** + * Customized text for the terms of use link (default is "Background Terms of Use") + */ + "terms-of-use-text"?: string + + /** + * Specify a checksum for tiles, which aren't real tiles. `type` is the digest type and can be MD5, SHA-1, SHA-256, SHA-384 and SHA-512, value is the hex encoded checksum in lower case. To create a checksum save the tile as file and upload it to e.g. https://defuse.ca/checksums.htm. + */ + "no-tile-checksum"?: string + + /** + * header-name attribute specifies a header returned by tile server, that will be shown as `metadata-key` attribute in Show Tile Info dialog + */ + "metadata-header"?: string + + /** + * Set to `true` if imagery source is properly aligned and does not need imagery offset adjustments. This is used for OSM based sources too. + */ + "valid-georeference"?: boolean + + /** + * Size of individual tiles delivered by a TMS service + */ + "tile-size"?: number + + /** + * Whether tiles status can be accessed by appending /status to the tile URL and can be submitted for re-rendering by appending /dirty. + */ + "mod-tile-features"?: string + + /** + * HTTP headers to be sent to server. It has two attributes header-name and header-value. May be specified multiple times. + */ + "custom-http-headers"?: { "header-name": string; "header-value": string }[] + + /** + * Default layer to open (when using WMS_ENDPOINT type). Contains list of layer tag with two attributes - name and style, e.g. `\"default-layers\": [\"layer\": { name=\"Basisdata_NP_Basiskart_JanMayen_WMTS_25829\" \"style\":\"default\" } ]` (not allowed in `mirror` attribute) + */ + "default-layers"?: { layer: { "layer-name": string; "layer-style": string } }[] + + /** + * format to use when connecting tile server (when using WMS_ENDPOINT type) + */ + format?: string + + /** + * If `true` transparent tiles will be requested from WMS server + */ + transparent?: boolean + + /** + * minimum expiry time for tiles in seconds. The larger the value, the longer entry in cache will be considered valid + */ + "minimum-tile-expire"?: number + } +} diff --git a/scripts/GenerateSeries.ts b/scripts/GenerateSeries.ts index 6ed84955fc..3017e81806 100644 --- a/scripts/GenerateSeries.ts +++ b/scripts/GenerateSeries.ts @@ -1,10 +1,10 @@ import { existsSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "fs" import ScriptUtils from "./ScriptUtils" -import { Utils } from "../Utils" +import { Utils } from "../src/Utils" import Script from "./Script" -import { GeoOperations } from "../Logic/GeoOperations" +import { GeoOperations } from "../src/Logic/GeoOperations" import { Feature, Polygon } from "geojson" -import { Tiles } from "../Models/TileRange" +import { Tiles } from "../src/Models/TileRange" class StatsDownloader { private readonly urlTemplate = diff --git a/scripts/build.sh b/scripts/build.sh index 36fabab42b..3b1cf07b8c 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -28,22 +28,22 @@ BRANCH=`git rev-parse --abbrev-ref HEAD` echo "The branch name is $BRANCH" if [ $BRANCH = "develop" ] then - SRC_MAPS="--sourcemap" - echo "Source maps are enabled" + # SRC_MAPS="--sourcemap" + echo "Source maps are NOT enabled as they consume to much RAM" fi -if [ $BRANCH = "master" ] +if [ $BRANCH = "master" ] || [ $BRANCH = "develop" ] then ASSET_URL="./" export ASSET_URL echo "$ASSET_URL" else - ASSET_URL="mc/$BRANCH" + ASSET_URL="$BRANCH" export ASSET_URL echo "$ASSET_URL" fi -export NODE_OPTIONS=--max-old-space-size=6500 +export NODE_OPTIONS=--max-old-space-size=7000 vite build $SRC_MAPS diff --git a/scripts/downloadEli.ts b/scripts/downloadEli.ts index 39f903ba29..97758888bb 100644 --- a/scripts/downloadEli.ts +++ b/scripts/downloadEli.ts @@ -1,6 +1,6 @@ import Script from "./Script" import { Utils } from "../src/Utils" -import { FeatureCollection } from "geojson" +import { Eli, EliEntry } from "./@types/eli" import fs from "fs" class DownloadEli extends Script { @@ -12,8 +12,8 @@ class DownloadEli extends Script { // Target should use '.json' instead of '.geojson', as the latter cannot be imported by the build systems const target = args[0] ?? "src/assets/editor-layer-index.json" - const eli = await Utils.downloadJson(url) - const keptLayers = [] + const eli: Eli = await Utils.downloadJson(url) + const keptLayers: EliEntry[] = [] console.log("Got", eli.features.length, "ELI-entries") for (let layer of eli.features) { const props = layer.properties @@ -45,11 +45,11 @@ class DownloadEli extends Script { continue } - if (props.permission_url === "no") { + if (props.permission_osm === "no") { continue } - if (props.max_zoom < 19) { + if (props.max_zoom && props.max_zoom < 19) { // We want users to zoom to level 19 when adding a point // If they are on a layer which hasn't enough precision, they can not zoom far enough. This is confusing, so we don't use this layer continue @@ -60,24 +60,24 @@ class DownloadEli extends Script { continue } - const keptKeys = [ - "name", - "id", - "url", - "attribution", - "type", - "category", - "min_zoom", - "max_zoom", - "best", - "default", - "tile-size", - ] - layer.properties = {} - for (const keptKey of keptKeys) { - if (props[keptKey]) { - layer.properties[keptKey] = props[keptKey] - } + if (props.url.startsWith("http://")) { + // Mixed content will not work properly, so we don't use this layer + continue + } + + // Override the layer, so it contains only the properties we need + layer.properties = { + name: props.name, + id: props.id, + url: props.url, + attribution: props.attribution, + type: props.type, + category: props.category, + min_zoom: props.min_zoom, + max_zoom: props.max_zoom, + best: props.best ? true : undefined, + default: props.default ? true : undefined, + "tile-size": props["tile-size"], } layer = { properties: layer.properties, type: layer.type, geometry: layer.geometry } diff --git a/scripts/fixQuestionHint.ts b/scripts/fixQuestionHint.ts index e7b0a1b1eb..1406d2b324 100644 --- a/scripts/fixQuestionHint.ts +++ b/scripts/fixQuestionHint.ts @@ -1,10 +1,10 @@ import * as fs from "fs" -import { DesugaringStep } from "../Models/ThemeConfig/Conversion/Conversion" -import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" -import { QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" +import { DesugaringStep } from "../src/Models/ThemeConfig/Conversion/Conversion" +import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" +import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" import * as fakedom from "fake-dom" import Script from "./Script" -import { FixedUiElement } from "../UI/Base/FixedUiElement" +import { FixedUiElement } from "../src/UI/Base/FixedUiElement" class ExtractQuestionHint extends DesugaringStep { constructor() { diff --git a/scripts/generateLayerOverview.ts b/scripts/generateLayerOverview.ts index 8cf68d9ff6..1314f3578c 100644 --- a/scripts/generateLayerOverview.ts +++ b/scripts/generateLayerOverview.ts @@ -1,26 +1,26 @@ -import ScriptUtils from "./ScriptUtils" -import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs" -import licenses from "../src/assets/generated/license_info.json" -import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson" -import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" -import Constants from "../src/Models/Constants" +import ScriptUtils from "./ScriptUtils"; +import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs"; +import licenses from "../src/assets/generated/license_info.json"; +import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson"; +import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson"; +import Constants from "../src/Models/Constants"; import { DetectDuplicateFilters, DoesImageExist, PrevalidateTheme, ValidateLayer, - ValidateThemeAndLayers, -} from "../src/Models/ThemeConfig/Conversion/Validation" -import { Translation } from "../src/UI/i18n/Translation" -import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson" -import PointRenderingConfigJson from "../src/Models/ThemeConfig/Json/PointRenderingConfigJson" -import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer" -import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme" -import { DesugaringContext } from "../src/Models/ThemeConfig/Conversion/Conversion" -import { Utils } from "../src/Utils" -import Script from "./Script" -import { AllSharedLayers } from "../src/Customizations/AllSharedLayers" - + ValidateThemeAndLayers +} from "../src/Models/ThemeConfig/Conversion/Validation"; +import { Translation } from "../src/UI/i18n/Translation"; +import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson"; +import PointRenderingConfigJson from "../src/Models/ThemeConfig/Json/PointRenderingConfigJson"; +import { PrepareLayer } from "../src/Models/ThemeConfig/Conversion/PrepareLayer"; +import { PrepareTheme } from "../src/Models/ThemeConfig/Conversion/PrepareTheme"; +import { DesugaringContext } from "../src/Models/ThemeConfig/Conversion/Conversion"; +import { Utils } from "../src/Utils"; +import Script from "./Script"; +import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"; +import {parse as parse_html} from "node-html-parser" // This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files. // It spits out an overview of those to be used to load them @@ -516,7 +516,10 @@ class LayerOverviewUtils extends Script { hideFromOverview: t.hideFromOverview ?? false, shortDescription: t.shortDescription ?? - new Translation(t.description).FirstSentence().translations, + new Translation(t.description) + .FirstSentence() + .OnEveryLanguage(s => parse_html(s).innerText) + .translations, mustHaveLanguage: t.mustHaveLanguage?.length > 0, } }) diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index 1fbbbc1458..102bb709bc 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -86,7 +86,7 @@ export class Mapillary extends ImageProvider { public async DownloadAttribution(url: string): Promise { const license = new LicenseInfo() - license.artist = "Contributor name unavailable" + license.artist = undefined license.license = "CC BY-SA 4.0" // license.license = "Creative Commons Attribution-ShareAlike 4.0 International License"; license.attributionRequired = true diff --git a/src/Logic/Osm/Actions/ChangeTagAction.ts b/src/Logic/Osm/Actions/ChangeTagAction.ts index 1abfb67989..72fbffab4d 100644 --- a/src/Logic/Osm/Actions/ChangeTagAction.ts +++ b/src/Logic/Osm/Actions/ChangeTagAction.ts @@ -28,7 +28,7 @@ export default class ChangeTagAction extends OsmChangeAction { currentTags: Record, meta: { theme: string - changeType: "answer" | "soft-delete" | "add-image" | string + changeType: "answer" | "soft-delete" | "add-image" | "link-image" | string } ) { super(elementId, true) diff --git a/src/Logic/Osm/Actions/LinkPicture.ts b/src/Logic/Osm/Actions/LinkPicture.ts new file mode 100644 index 0000000000..014a836a06 --- /dev/null +++ b/src/Logic/Osm/Actions/LinkPicture.ts @@ -0,0 +1,32 @@ +import ChangeTagAction from "./ChangeTagAction" +import { Tag } from "../../Tags/Tag" + +export default class LinkPicture extends ChangeTagAction { + /** + * Adds a link to an image + * @param elementId + * @param proposedKey: a key which might be used, typically `image`. If the key is already used with a different URL, `key+":0"` will be used instead (or a higher number if needed) + * @param url + * @param currentTags + * @param meta + * + */ + constructor( + elementId: string, + proposedKey: "image" | "mapillary" | "wiki_commons" | string, + url: string, + currentTags: Record, + meta: { + theme: string + changeType: "add-image" | "link-image" + } + ) { + let key = proposedKey + let i = 0 + while (currentTags[key] !== undefined && currentTags[key] !== url) { + key = proposedKey + ":" + i + i++ + } + super(elementId, new Tag(key, url), currentTags, meta) + } +} diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts index a14d308497..b4b47d68e9 100644 --- a/src/Logic/Osm/OsmConnection.ts +++ b/src/Logic/Osm/OsmConnection.ts @@ -1,8 +1,10 @@ -import osmAuth from "osm-auth" +// @ts-ignore +import { osmAuth } from "osm-auth" import { Store, Stores, UIEventSource } from "../UIEventSource" import { OsmPreferences } from "./OsmPreferences" import { Utils } from "../../Utils" - +import { LocalStorageSource } from "../Web/LocalStorageSource" +import * as config from "../../../package.json" export default class UserDetails { public loggedIn = false public name = "Not logged in" @@ -22,23 +24,18 @@ export default class UserDetails { } } +export interface AuthConfig { + "#"?: string // optional comment + oauth_client_id: string + oauth_secret: string + url: string +} + export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable" export class OsmConnection { - public static readonly oauth_configs = { - osm: { - oauth_consumer_key: "hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem", - oauth_secret: "wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI", - url: "https://www.openstreetmap.org", - // OAUTH 1.0 application - // https://www.openstreetmap.org/user/Pieter%20Vander%20Vennet/oauth_clients/7404 - }, - "osm-test": { - oauth_consumer_key: "Zgr7EoKb93uwPv2EOFkIlf3n9NLwj5wbyfjZMhz2", - oauth_secret: "3am1i1sykHDMZ66SGq4wI2Z7cJMKgzneCHp3nctn", - url: "https://master.apis.dev.openstreetmap.org", - }, - } + public static readonly oauth_configs: Record = + config.config.oauth_credentials public auth public userDetails: UIEventSource public isLoggedIn: Store @@ -53,11 +50,7 @@ export class OsmConnection { "not-attempted" ) public preferencesHandler: OsmPreferences - public readonly _oauth_config: { - oauth_consumer_key: string - oauth_secret: string - url: string - } + public readonly _oauth_config: AuthConfig private readonly _dryRun: Store private fakeUser: boolean private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [] @@ -83,6 +76,19 @@ export class OsmConnection { console.debug("Using backend", this._oauth_config.url) this._iframeMode = Utils.runningFromConsole ? false : window !== window.top + // Check if there are settings available in environment variables, and if so, use those + if ( + import.meta.env.VITE_OSM_OAUTH_CLIENT_ID !== undefined && + import.meta.env.VITE_OSM_OAUTH_SECRET !== undefined + ) { + console.debug("Using environment variables for oauth config") + this._oauth_config = { + oauth_client_id: import.meta.env.VITE_OSM_OAUTH_CLIENT_ID, + oauth_secret: import.meta.env.VITE_OSM_OAUTH_SECRET, + url: "https://www.openstreetmap.org", + } + } + this.userDetails = new UIEventSource( new UserDetails(this._oauth_config.url), "userDetails" @@ -190,6 +196,9 @@ export class OsmConnection { const self = this console.log("Trying to log in...") this.updateAuthObject() + LocalStorageSource.Get("location_before_login").setData( + Utils.runningFromConsole ? undefined : window.location.href + ) this.auth.xhr( { method: "GET", @@ -202,13 +211,8 @@ export class OsmConnection { if (err.status == 401) { console.log("Clearing tokens...") // Not authorized - our token probably got revoked - // Reset all the tokens - const tokens = [ - "https://www.openstreetmap.orgoauth_request_token_secret", - "https://www.openstreetmap.orgoauth_token", - "https://www.openstreetmap.orgoauth_token_secret", - ] - tokens.forEach((token) => localStorage.removeItem(token)) + self.auth.logout() + self.LogOut() } return } @@ -310,6 +314,7 @@ export class OsmConnection { ): Promise { return await this.interact(path, "POST", header, content) } + public async put( path: string, content?: string, @@ -486,15 +491,29 @@ export class OsmConnection { // Same for an iframe... this.auth = new osmAuth({ - oauth_consumer_key: this._oauth_config.oauth_consumer_key, - oauth_secret: this._oauth_config.oauth_secret, + client_id: this._oauth_config.oauth_client_id, url: this._oauth_config.url, - landing: standalone ? undefined : window.location.href, + scope: "read_prefs write_prefs write_api write_gpx write_notes", + redirect_uri: Utils.runningFromConsole + ? "https://mapcomplete.org/land.html" + : window.location.protocol + "//" + window.location.host + "/land.html", singlepage: !standalone, auto: true, }) } + /** + * To be called by land.html + */ + public finishLogin(callback: (previousURL: string) => void) { + this.auth.authenticate(function () { + // Fully authed at this point + console.log("Authentication successful!") + const previousLocation = LocalStorageSource.Get("location_before_login") + callback(previousLocation.data) + }) + } + private CheckForMessagesContinuously() { const self = this if (this.isChecking) { diff --git a/src/Logic/Web/NearbyImagesSearch.ts b/src/Logic/Web/NearbyImagesSearch.ts new file mode 100644 index 0000000000..665cc66e76 --- /dev/null +++ b/src/Logic/Web/NearbyImagesSearch.ts @@ -0,0 +1,222 @@ +import { IndexedFeatureSource } from "../FeatureSource/FeatureSource" +import { GeoOperations } from "../GeoOperations" +import { ImmutableStore, Store, Stores, UIEventSource } from "../UIEventSource" +import { Mapillary } from "../ImageProviders/Mapillary" +import P4C from "pic4carto" +import { Utils } from "../../Utils" +export interface NearbyImageOptions { + lon: number + lat: number + // Radius of the upstream search + searchRadius?: 500 | number + maxDaysOld?: 1095 | number + blacklist: Store<{ url: string }[]> + shownImagesCount?: UIEventSource + towardscenter?: UIEventSource + allowSpherical?: UIEventSource + // Radius of what is shown. Useless to select a value > searchRadius; defaults to searchRadius + shownRadius?: UIEventSource +} + +export interface P4CPicture { + pictureUrl: string + date?: number + coordinates: { lat: number; lng: number } + provider: "Mapillary" | string + author? + license? + detailsUrl?: string + direction? + osmTags?: object /*To copy straight into OSM!*/ + thumbUrl: string + details: { + isSpherical: boolean + } +} + +/** + * Uses Pic4wCarto to fetch nearby images from various providers + */ +export default class NearbyImagesSearch { + private static readonly services = [ + "mapillary", + "flickr", + "openstreetcam", + "wikicommons", + ] as const + + private individualStores + private readonly _store: UIEventSource = new UIEventSource([]) + public readonly store: Store = this._store + private readonly _options: NearbyImageOptions + + constructor(options: NearbyImageOptions, features: IndexedFeatureSource) { + this.individualStores = NearbyImagesSearch.services.map((s) => + NearbyImagesSearch.buildPictureFetcher(options, s) + ) + this._options = options + if (features !== undefined) { + const osmImages = new ImagesInLoadedDataFetcher(features).fetchAround({ + lat: options.lat, + lon: options.lon, + searchRadius: options.searchRadius ?? 100, + }) + this.individualStores.push( + new ImmutableStore({ images: osmImages, beforeFilter: osmImages.length }) + ) + } + for (const source of this.individualStores) { + source.addCallback(() => this.update()) + } + this.update() + } + + private static buildPictureFetcher( + options: NearbyImageOptions, + fetcher: "mapillary" | "flickr" | "openstreetcam" | "wikicommons" + ): Store<{ images: P4CPicture[]; beforeFilter: number }> { + const picManager = new P4C.PicturesManager({ usefetchers: [fetcher] }) + const searchRadius = options.searchRadius ?? 100 + const maxAgeSeconds = (options.maxDaysOld ?? 3 * 365) * 24 * 60 * 60 * 1000 + + const p4cStore = Stores.FromPromise( + picManager.startPicsRetrievalAround( + new P4C.LatLng(options.lat, options.lon), + searchRadius, + { + mindate: new Date().getTime() - maxAgeSeconds, + towardscenter: false, + } + ) + ) + return p4cStore.map( + (images) => { + if (images === undefined) { + return undefined + } + const beforeFilterCount = images.length + if (!options?.allowSpherical?.data) { + images = images?.filter((i) => i.details.isSpherical !== true) + } + + const shownRadius = options?.shownRadius?.data ?? searchRadius + if (shownRadius !== searchRadius) { + images = images.filter((i) => { + const d = GeoOperations.distanceBetween( + [i.coordinates.lng, i.coordinates.lat], + [options.lon, options.lat] + ) + return d <= shownRadius + }) + } + if (options.towardscenter?.data) { + images = images.filter((i) => { + if (i.direction === undefined || isNaN(i.direction)) { + return false + } + const bearing = GeoOperations.bearing( + [i.coordinates.lng, i.coordinates.lat], + [options.lon, options.lat] + ) + const diff = Math.abs((i.direction - bearing) % 360) + return diff < 40 + }) + } + + images?.sort((a, b) => { + const distanceA = GeoOperations.distanceBetween( + [a.coordinates.lng, a.coordinates.lat], + [options.lon, options.lat] + ) + const distanceB = GeoOperations.distanceBetween( + [b.coordinates.lng, b.coordinates.lat], + [options.lon, options.lat] + ) + return distanceA - distanceB + }) + + return { images, beforeFilter: beforeFilterCount } + }, + [options.blacklist, options.allowSpherical, options.towardscenter, options.shownRadius] + ) + } + + private update() { + const seen: Set = new Set(this._options.blacklist.data.map((d) => d.url)) + let beforeFilter = 0 + let result: P4CPicture[] = [] + for (const source of this.individualStores) { + const imgs = source.data + if (imgs === undefined) { + continue + } + beforeFilter = beforeFilter + imgs.beforeFilter + for (const img of imgs.images) { + if (seen.has(img.pictureUrl)) { + continue + } + seen.add(img.pictureUrl) + result.push(img) + } + } + const c = [this._options.lon, this._options.lat] + result.sort((a, b) => { + const da = GeoOperations.distanceBetween([a.coordinates.lng, a.coordinates.lat], c) + const db = GeoOperations.distanceBetween([b.coordinates.lng, b.coordinates.lat], c) + return da - db + }) + if (Utils.sameList(result, this._store.data)) { + // return + } + this._store.setData(result) + } +} + +/** + * Extracts pictures from currently loaded features + */ +class ImagesInLoadedDataFetcher { + private indexedFeatures: IndexedFeatureSource + + constructor(indexedFeatures: IndexedFeatureSource) { + this.indexedFeatures = indexedFeatures + } + + public fetchAround(loc: { lon: number; lat: number; searchRadius?: number }): P4CPicture[] { + const foundImages: P4CPicture[] = [] + this.indexedFeatures.features.data.forEach((feature) => { + const props = feature.properties + const images = [] + if (props.image) { + images.push(props.image) + } + for (let i = 0; i < 10; i++) { + if (props["image:" + i]) { + images.push(props["image:" + i]) + } + } + if (images.length == 0) { + return + } + const centerpoint = GeoOperations.centerpointCoordinates(feature) + const d = GeoOperations.distanceBetween(centerpoint, [loc.lon, loc.lat]) + if (loc.searchRadius !== undefined && d > loc.searchRadius) { + return + } + for (const image of images) { + foundImages.push({ + pictureUrl: image, + thumbUrl: image, + coordinates: { lng: centerpoint[0], lat: centerpoint[1] }, + provider: "OpenStreetMap", + details: { + isSpherical: false, + }, + osmTags: { image }, + }) + } + }) + + return foundImages + } +} diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index ad7e9a62d0..2d7a87e741 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -1,14 +1,13 @@ -import { Utils } from "../Utils" import * as meta from "../../package.json" +import { Utils } from "../Utils" export type PriviligedLayerType = (typeof Constants.priviliged_layers)[number] export default class Constants { public static vNumber = meta.version - public static ImgurApiKey = "7070e7167f0a25a" - public static readonly mapillary_client_token_v4 = - "MLY|4441509239301885|b40ad2d3ea105435bd40c7e76993ae85" + public static ImgurApiKey = meta.config.api_keys.imgur + public static readonly mapillary_client_token_v4 = meta.config.api_keys.mapillary_v4 /** * API key for Maproulette @@ -19,15 +18,7 @@ export default class Constants { */ public static readonly MaprouletteApiKey = "" - public static defaultOverpassUrls = [ - // The official instance, 10000 queries per day per project allowed - "https://overpass-api.de/api/interpreter", - // 'Fair usage' - "https://overpass.kumi.systems/api/interpreter", - // Offline: "https://overpass.nchc.org.tw/api/interpreter", - "https://overpass.openstreetmap.ru/cgi/interpreter", - // Doesn't support nwr: "https://overpass.openstreetmap.fr/api/interpreter" - ] + public static defaultOverpassUrls = meta.config.default_overpass_urls public static readonly added_by_default = [ "selected_element", @@ -100,6 +91,7 @@ export default class Constants { "etymology", "food", "cafes_and_pubs", + "shops", "playgrounds", "hailhydrant", "toilets", @@ -113,9 +105,8 @@ export default class Constants { * In seconds */ static zoomToLocationTimeout = 15 - static countryCoderEndpoint: string = - "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/latlon2country" - public static readonly OsmPreferenceKeyPicturesLicense = "pictures-license" + static countryCoderEndpoint: string = meta.config.country_coder_host + /** * These are the values that are allowed to use as 'backdrop' icon for a map pin */ diff --git a/src/Models/ThemeConfig/Conversion/Validation.ts b/src/Models/ThemeConfig/Conversion/Validation.ts index 89fe06697a..d51ae560eb 100644 --- a/src/Models/ThemeConfig/Conversion/Validation.ts +++ b/src/Models/ThemeConfig/Conversion/Validation.ts @@ -1,42 +1,43 @@ -import {DesugaringStep, Each, Fuse, On} from "./Conversion" -import {LayerConfigJson} from "../Json/LayerConfigJson" -import LayerConfig from "../LayerConfig" -import {Utils} from "../../../Utils" -import Constants from "../../Constants" -import {Translation} from "../../../UI/i18n/Translation" -import {LayoutConfigJson} from "../Json/LayoutConfigJson" -import LayoutConfig from "../LayoutConfig" -import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson" -import {TagUtils} from "../../../Logic/Tags/TagUtils" -import {ExtractImages} from "./FixImages" -import {And} from "../../../Logic/Tags/And" -import Translations from "../../../UI/i18n/Translations" -import Svg from "../../../Svg" -import FilterConfigJson from "../Json/FilterConfigJson" -import DeleteConfig from "../DeleteConfig" -import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson" -import Validators from "../../../UI/InputElement/Validators" +import { DesugaringStep, Each, Fuse, On } from "./Conversion"; +import { LayerConfigJson } from "../Json/LayerConfigJson"; +import LayerConfig from "../LayerConfig"; +import { Utils } from "../../../Utils"; +import Constants from "../../Constants"; +import { Translation } from "../../../UI/i18n/Translation"; +import { LayoutConfigJson } from "../Json/LayoutConfigJson"; +import LayoutConfig from "../LayoutConfig"; +import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"; +import { TagUtils } from "../../../Logic/Tags/TagUtils"; +import { ExtractImages } from "./FixImages"; +import { And } from "../../../Logic/Tags/And"; +import Translations from "../../../UI/i18n/Translations"; +import Svg from "../../../Svg"; +import FilterConfigJson from "../Json/FilterConfigJson"; +import DeleteConfig from "../DeleteConfig"; +import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"; +import Validators from "../../../UI/InputElement/Validators"; import TagRenderingConfig from "../TagRenderingConfig"; +import { parse as parse_html } from "node-html-parser"; class ValidateLanguageCompleteness extends DesugaringStep { - private readonly _languages: string[] + private readonly _languages: string[]; constructor(...languages: string[]) { super( "Checks that the given object is fully translated in the specified languages", [], "ValidateLanguageCompleteness" - ) - this._languages = languages ?? ["en"] + ); + this._languages = languages ?? ["en"]; } convert( obj: any, context: string ): { result: LayerConfig; errors: string[]; warnings: string[] } { - const errors = [] - const warnings: string[] = [] - const translations = Translation.ExtractAllTranslationsFrom(obj) + const errors = []; + const warnings: string[] = []; + const translations = Translation.ExtractAllTranslationsFrom(obj); for (const neededLanguage of this._languages) { translations .filter( @@ -53,32 +54,32 @@ class ValidateLanguageCompleteness extends DesugaringStep { missing.context + ".\n\tThe known translation is " + missing.tr.textFor("en") - ) - }) + ); + }); } return { result: obj, errors, - warnings, - } + warnings + }; } } export class DoesImageExist extends DesugaringStep { - private readonly _knownImagePaths: Set - private readonly _ignore?: Set - private readonly doesPathExist: (path: string) => boolean = undefined + private readonly _knownImagePaths: Set; + private readonly _ignore?: Set; + private readonly doesPathExist: (path: string) => boolean = undefined; constructor( knownImagePaths: Set, checkExistsSync: (path: string) => boolean = undefined, ignore?: Set ) { - super("Checks if an image exists", [], "DoesImageExist") - this._ignore = ignore - this._knownImagePaths = knownImagePaths - this.doesPathExist = checkExistsSync + super("Checks if an image exists", [], "DoesImageExist"); + this._ignore = ignore; + this._knownImagePaths = knownImagePaths; + this.doesPathExist = checkExistsSync; } convert( @@ -86,53 +87,53 @@ export class DoesImageExist extends DesugaringStep { context: string ): { result: string; errors?: string[]; warnings?: string[]; information?: string[] } { if (this._ignore?.has(image)) { - return {result: image} + return { result: image }; } - const errors = [] - const warnings = [] - const information = [] + const errors = []; + const warnings = []; + const information = []; if (image.indexOf("{") >= 0) { - information.push("Ignoring image with { in the path: " + image) - return {result: image} + information.push("Ignoring image with { in the path: " + image); + return { result: image }; } if (image === "assets/SocialImage.png") { - return {result: image} + return { result: image }; } if (image.match(/[a-z]*/)) { if (Svg.All[image + ".svg"] !== undefined) { // This is a builtin img, e.g. 'checkmark' or 'crosshair' - return {result: image} + return { result: image }; } } if (image.startsWith("<") && image.endsWith(">")) { // This is probably HTML, you're on your own here - return {result: image} + return { result: image }; } if (!this._knownImagePaths.has(image)) { if (this.doesPathExist === undefined) { errors.push( `Image with path ${image} not found or not attributed; it is used in ${context}` - ) + ); } else if (!this.doesPathExist(image)) { errors.push( `Image with path ${image} does not exist; it is used in ${context}.\n Check for typo's and missing directories in the path.` - ) + ); } else { errors.push( `Image with path ${image} is not attributed (but it exists); execute 'npm run query:licenses' to add the license information and/or run 'npm run generate:licenses' to compile all the license info` - ) + ); } } return { result: image, errors, warnings, - information, - } + information + }; } } @@ -141,11 +142,11 @@ class ValidateTheme extends DesugaringStep { * The paths where this layer is originally saved. Triggers some extra checks * @private */ - private readonly _path?: string - private readonly _isBuiltin: boolean + private readonly _path?: string; + private readonly _isBuiltin: boolean; //private readonly _sharedTagRenderings: Map - private readonly _validateImage: DesugaringStep - private readonly _extractImages: ExtractImages = undefined + private readonly _validateImage: DesugaringStep; + private readonly _extractImages: ExtractImages = undefined; constructor( doesImageExist: DoesImageExist, @@ -153,12 +154,12 @@ class ValidateTheme extends DesugaringStep { isBuiltin: boolean, sharedTagRenderings?: Set ) { - super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme") - this._validateImage = doesImageExist - this._path = path - this._isBuiltin = isBuiltin + super("Doesn't change anything, but emits warnings and errors", [], "ValidateTheme"); + this._validateImage = doesImageExist; + this._path = path; + this._isBuiltin = isBuiltin; if (sharedTagRenderings) { - this._extractImages = new ExtractImages(this._isBuiltin, sharedTagRenderings) + this._extractImages = new ExtractImages(this._isBuiltin, sharedTagRenderings); } } @@ -166,11 +167,11 @@ class ValidateTheme extends DesugaringStep { json: LayoutConfigJson, context: string ): { result: LayoutConfigJson; errors: string[]; warnings: string[]; information: string[] } { - const errors = [] - const warnings = [] - const information = [] + const errors = []; + const warnings = []; + const information = []; - const theme = new LayoutConfig(json, this._isBuiltin) + const theme = new LayoutConfig(json, this._isBuiltin); { // Legacy format checks @@ -180,21 +181,21 @@ class ValidateTheme extends DesugaringStep { "The theme " + json.id + " has units defined - these should be defined on the layer instead. (Hint: use overrideAll: { '+units': ... }) " - ) + ); } if (json["roamingRenderings"] !== undefined) { errors.push( "Theme " + json.id + " contains an old 'roamingRenderings'. Use an 'overrideAll' instead" - ) + ); } } } if (this._isBuiltin && this._extractImages !== undefined) { // Check images: are they local, are the licenses there, is the theme icon square, ... - const images = this._extractImages.convertStrict(json, "validation") - const remoteImages = images.filter((img) => img.path.indexOf("http") == 0) + const images = this._extractImages.convertStrict(json, "validation"); + const remoteImages = images.filter((img) => img.path.indexOf("http") == 0); for (const remoteImage of remoteImages) { errors.push( "Found a remote image: " + @@ -202,7 +203,7 @@ class ValidateTheme extends DesugaringStep { " in theme " + json.id + ", please download it." - ) + ); } for (const image of images) { this._validateImage.convertJoin( @@ -211,20 +212,20 @@ class ValidateTheme extends DesugaringStep { errors, warnings, information - ) + ); } } try { if (this._isBuiltin) { if (theme.id !== theme.id.toLowerCase()) { - errors.push("Theme ids should be in lowercase, but it is " + theme.id) + errors.push("Theme ids should be in lowercase, but it is " + theme.id); } const filename = this._path.substring( this._path.lastIndexOf("/") + 1, this._path.length - 5 - ) + ); if (theme.id !== filename) { errors.push( "Theme ids should be the same as the name.json, but we got id: " + @@ -234,7 +235,7 @@ class ValidateTheme extends DesugaringStep { " (" + this._path + ")" - ) + ); } this._validateImage.convertJoin( theme.icon, @@ -242,44 +243,44 @@ class ValidateTheme extends DesugaringStep { errors, warnings, information - ) + ); } - const dups = Utils.Dupiclates(json.layers.map((layer) => layer["id"])) + const dups = Utils.Dupiclates(json.layers.map((layer) => layer["id"])); if (dups.length > 0) { errors.push( `The theme ${json.id} defines multiple layers with id ${dups.join(", ")}` - ) + ); } if (json["mustHaveLanguage"] !== undefined) { const checked = new ValidateLanguageCompleteness( ...json["mustHaveLanguage"] - ).convert(theme, theme.id) + ).convert(theme, theme.id); - errors.push(...checked.errors) + errors.push(...checked.errors); } if (!json.hideFromOverview && theme.id !== "personal" && this._isBuiltin) { // The first key in the the title-field must be english, otherwise the title in the loading page will be the different language - const targetLanguage = theme.title.SupportedLanguages()[0] + const targetLanguage = theme.title.SupportedLanguages()[0]; if (targetLanguage !== "en") { warnings.push( `TargetLanguage is not 'en' for public theme ${theme.id}, it is ${targetLanguage}. Move 'en' up in the title of the theme and set it as the first key` - ) + ); } // Official, public themes must have a full english translation - const checked = new ValidateLanguageCompleteness("en").convert(theme, theme.id) - errors.push(...checked.errors) + const checked = new ValidateLanguageCompleteness("en").convert(theme, theme.id); + errors.push(...checked.errors); } } catch (e) { - errors.push(e) + errors.push(e); } return { result: json, errors, warnings, - information, - } + information + }; } } @@ -294,7 +295,7 @@ export class ValidateThemeAndLayers extends Fuse { "Validates a theme and the contained layers", new ValidateTheme(doesImageExist, path, isBuiltin, sharedTagRenderings), new On("layers", new Each(new ValidateLayer(undefined, isBuiltin, doesImageExist))) - ) + ); } } @@ -304,26 +305,26 @@ class OverrideShadowingCheck extends DesugaringStep { "Checks that an 'overrideAll' does not override a single override", [], "OverrideShadowingCheck" - ) + ); } convert( json: LayoutConfigJson, _: string ): { result: LayoutConfigJson; errors?: string[]; warnings?: string[] } { - const overrideAll = json.overrideAll + const overrideAll = json.overrideAll; if (overrideAll === undefined) { - return {result: json} + return { result: json }; } - const errors = [] - const withOverride = json.layers.filter((l) => l["override"] !== undefined) + const errors = []; + const withOverride = json.layers.filter((l) => l["override"] !== undefined); for (const layer of withOverride) { for (const key in overrideAll) { if (key.endsWith("+") || key.startsWith("+")) { // This key will _add_ to the list, not overwrite it - so no warning is needed - continue + continue; } if ( layer["override"][key] !== undefined || @@ -334,19 +335,19 @@ class OverrideShadowingCheck extends DesugaringStep { JSON.stringify(layer["builtin"]) + " has a shadowed property: " + key + - " is overriden by overrideAll of the theme" - errors.push(w) + " is overriden by overrideAll of the theme"; + errors.push(w); } } } - return {result: json, errors} + return { result: json, errors }; } } class MiscThemeChecks extends DesugaringStep { constructor() { - super("Miscelleanous checks on the theme", [], "MiscThemesChecks") + super("Miscelleanous checks on the theme", [], "MiscThemesChecks"); } convert( @@ -358,19 +359,19 @@ class MiscThemeChecks extends DesugaringStep { warnings?: string[] information?: string[] } { - const warnings = [] - const errors = [] + const warnings = []; + const errors = []; if (json.id !== "personal" && (json.layers === undefined || json.layers.length === 0)) { - errors.push("The theme " + json.id + " has no 'layers' defined (" + context + ")") + errors.push("The theme " + json.id + " has no 'layers' defined (" + context + ")"); } if (json.socialImage === "") { - warnings.push("Social image for theme " + json.id + " is the emtpy string") + warnings.push("Social image for theme " + json.id + " is the emtpy string"); } return { result: json, warnings, - errors, - } + errors + }; } } @@ -380,47 +381,57 @@ export class PrevalidateTheme extends Fuse { "Various consistency checks on the raw JSON", new MiscThemeChecks(), new OverrideShadowingCheck() - ) + ); } } export class DetectConflictingAddExtraTags extends DesugaringStep { constructor() { - super("The `if`-part in a mapping might set some keys. Those key are not allowed to be set in the `addExtraTags`, as this might result in conflicting values", [], "DetectConflictingAddExtraTags"); + super( + "The `if`-part in a mapping might set some keys. Those key are not allowed to be set in the `addExtraTags`, as this might result in conflicting values", + [], + "DetectConflictingAddExtraTags" + ); } - convert(json: TagRenderingConfigJson, context: string): { - result: TagRenderingConfigJson; - errors?: string[]; - warnings?: string[]; + convert( + json: TagRenderingConfigJson, + context: string + ): { + result: TagRenderingConfigJson + errors?: string[] + warnings?: string[] information?: string[] } { - if (!(json.mappings?.length > 0)) { - return {result: json} + return { result: json }; } - const tagRendering = new TagRenderingConfig(json) + const tagRendering = new TagRenderingConfig(json); - const errors = [] + const errors = []; for (let i = 0; i < tagRendering.mappings.length; i++) { const mapping = tagRendering.mappings[i]; if (!mapping.addExtraTags) { - continue + continue; } - const keysInMapping = new Set(mapping.if.usedKeys()) + const keysInMapping = new Set(mapping.if.usedKeys()); - const keysInAddExtraTags = mapping.addExtraTags.map(t => t.key) + const keysInAddExtraTags = mapping.addExtraTags.map((t) => t.key); - const duplicateKeys = keysInAddExtraTags.filter(k => keysInMapping.has(k)) + const duplicateKeys = keysInAddExtraTags.filter((k) => keysInMapping.has(k)); if (duplicateKeys.length > 0) { errors.push( - "At " + context + ".mappings[" + i + "]: AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " + duplicateKeys.join(", ") - ) + "At " + + context + + ".mappings[" + + i + + "]: AddExtraTags overrides a key that is set in the `if`-clause of this mapping. Selecting this answer might thus first set one value (needed to match as answer) and then override it with a different value, resulting in an unsaveable question. The offending `addExtraTags` is " + + duplicateKeys.join(", ") + ); } } - return { result: json, errors @@ -428,13 +439,12 @@ export class DetectConflictingAddExtraTags extends DesugaringStep { - private readonly _calculatedTagNames: string[] + private readonly _calculatedTagNames: string[]; constructor(layerConfig?: LayerConfigJson) { - super("Checks that the mappings don't shadow each other", [], "DetectShadowedMappings") - this._calculatedTagNames = DetectShadowedMappings.extractCalculatedTagNames(layerConfig) + super("Checks that the mappings don't shadow each other", [], "DetectShadowedMappings"); + this._calculatedTagNames = DetectShadowedMappings.extractCalculatedTagNames(layerConfig); } /** @@ -448,11 +458,11 @@ export class DetectShadowedMappings extends DesugaringStep { if (ct.indexOf(":=") >= 0) { - return ct.split(":=")[0] + return ct.split(":=")[0]; } - return ct.split("=")[0] + return ct.split("=")[0]; }) ?? [] - ) + ); } /** @@ -492,40 +502,40 @@ export class DetectShadowedMappings extends DesugaringStep { - const ctx = `${context}.mappings[${i}]` - const ifTags = TagUtils.Tag(m.if, ctx) - const hideInAnswer = m["hideInAnswer"] + const ctx = `${context}.mappings[${i}]`; + const ifTags = TagUtils.Tag(m.if, ctx); + const hideInAnswer = m["hideInAnswer"]; if (hideInAnswer !== undefined && hideInAnswer !== false && hideInAnswer !== true) { - let conditionTags = TagUtils.Tag(hideInAnswer) + let conditionTags = TagUtils.Tag(hideInAnswer); // Merge the condition too! - return new And([conditionTags, ifTags]) + return new And([conditionTags, ifTags]); } - return ifTags - }) + return ifTags; + }); for (let i = 0; i < json.mappings.length; i++) { if (!parsedConditions[i].isUsableAsAnswer()) { // There is no straightforward way to convert this mapping.if into a properties-object, so we simply skip this one // Yes, it might be shadowed, but running this check is to difficult right now - continue + continue; } - const keyValues = parsedConditions[i].asChange(defaultProperties) - const properties = {} - keyValues.forEach(({k, v}) => { - properties[k] = v - }) + const keyValues = parsedConditions[i].asChange(defaultProperties); + const properties = {}; + keyValues.forEach(({ k, v }) => { + properties[k] = v; + }); for (let j = 0; j < i; j++) { - const doesMatch = parsedConditions[j].matchesProperties(properties) + const doesMatch = parsedConditions[j].matchesProperties(properties); if ( doesMatch && json.mappings[j]["hideInAnswer"] === true && @@ -533,7 +543,7 @@ export class DetectShadowedMappings extends DesugaringStep= 0 - const images = Utils.Dedup(Translations.T(mapping.then)?.ExtractImages() ?? []) - const ctx = `${context}.mappings[${i}]` + const mapping = json.mappings[i]; + const ignore = mapping["#"]?.indexOf(ignoreToken) >= 0; + const images = Utils.Dedup(Translations.T(mapping.then)?.ExtractImages() ?? []); + const ctx = `${context}.mappings[${i}]`; if (images.length > 0) { if (!ignore) { errors.push( `${ctx}: A mapping has an image in the 'then'-clause. Remove the image there and use \`"icon": \` instead. The images found are ${images.join( ", " )}. (This check can be turned of by adding "#": "${ignoreToken}" in the mapping, but this is discouraged` - ) + ); } else { information.push( `${ctx}: Ignored image ${images.join( ", " )} in 'then'-clause of a mapping as this check has been disabled` - ) + ); for (const image of images) { - this._doesImageExist.convertJoin(image, ctx, errors, warnings, information) + this._doesImageExist.convertJoin(image, ctx, errors, warnings, information); } } } else if (ignore) { - warnings.push(`${ctx}: unused '${ignoreToken}' - please remove this`) + warnings.push(`${ctx}: unused '${ignoreToken}' - please remove this`); } } @@ -645,17 +655,72 @@ export class DetectMappingsWithImages extends DesugaringStep> { + constructor() { + super("Given a possible set of translations, validates that does have `rel='noopener'` set", [], "ValidatePossibleLinks"); + } + + public isTabnabbingProne(str: string): boolean { + const p = parse_html(str); + const links = Array.from(p.getElementsByTagName("a")); + if (links.length == 0) { + return false; } + for (const link of Array.from(links)) { + if (link.getAttribute("target") !== "_blank") { + continue; + } + const rel = new Set(link.getAttribute("rel")?.split(" ") ?? []); + if (rel.has("noopener")) { + continue; + } + const source = link.getAttribute("href"); + if (source.startsWith("http")) { + // No variable part - we assume the link is safe + continue; + } + return true; + } + return false; + } + + convert(json: string | Record, context: string): { + result: string | Record; + errors?: string[]; + warnings?: string[]; + information?: string[] + } { + + const errors = []; + if (typeof json === "string") { + if (this.isTabnabbingProne(json)) { + errors.push("At " + context + ": the string " + json + " has a link targeting `_blank`, but it doesn't have `rel='noopener'` set. This gives rise to reverse tabnapping"); + } + } else { + for (const k in json) { + if (this.isTabnabbingProne(json[k])) { + errors.push(`At ${context}: the translation for ${k} '${json[k]}' has a link targeting \`_blank\`, but it doesn't have \`rel='noopener'\` set. This gives rise to reverse tabnapping`); + } + } + } + return { + errors, + result: json + }; } } class MiscTagRenderingChecks extends DesugaringStep { - private _options: { noQuestionHintCheck: boolean } + private _options: { noQuestionHintCheck: boolean }; constructor(options: { noQuestionHintCheck: boolean }) { - super("Miscellaneous checks on the tagrendering", ["special"], "MiscTagRenderingChecks") - this._options = options + super("Miscellaneous checks on the tagrendering", ["special"], "MiscTagRenderingChecks"); + this._options = options; } convert( @@ -667,25 +732,26 @@ class MiscTagRenderingChecks extends DesugaringStep { warnings?: string[] information?: string[] } { - const warnings = [] - const errors = [] + const warnings = []; + const errors = []; if (json["special"] !== undefined) { errors.push( "At " + context + - ': detected `special` on the top level. Did you mean `{"render":{ "special": ... }}`' - ) + ": detected `special` on the top level. Did you mean `{\"render\":{ \"special\": ... }}`" + ); } if (json["group"]) { errors.push( "At " + context + - ': groups are deprecated, use `"label": ["' + + ": groups are deprecated, use `\"label\": [\"" + json["group"] + - '"]` instead' - ) + "\"]` instead" + ); } - const freeformType = json["freeform"]?.["type"] + + const freeformType = json["freeform"]?.["type"]; if (freeformType) { if (Validators.availableTypes.indexOf(freeformType) < 0) { throw ( @@ -695,14 +761,14 @@ class MiscTagRenderingChecks extends DesugaringStep { freeformType + "; try one of " + Validators.availableTypes.join(", ") - ) + ); } } return { result: json, errors, - warnings, - } + warnings + }; } } @@ -717,8 +783,16 @@ export class ValidateTagRenderings extends Fuse { new DetectShadowedMappings(layerConfig), new DetectConflictingAddExtraTags(), new DetectMappingsWithImages(doesImageExist), + new On("render", + new ValidatePossibleLinks()), + new On("question", + new ValidatePossibleLinks()), + new On("questionHint", + new ValidatePossibleLinks()), + new On("mappings", + new Each(new On("then", new ValidatePossibleLinks()))), new MiscTagRenderingChecks(options) - ) + ); } } @@ -727,31 +801,31 @@ export class ValidateLayer extends DesugaringStep { * The paths where this layer is originally saved. Triggers some extra checks * @private */ - private readonly _path?: string - private readonly _isBuiltin: boolean - private readonly _doesImageExist: DoesImageExist + private readonly _path?: string; + private readonly _isBuiltin: boolean; + private readonly _doesImageExist: DoesImageExist; constructor(path: string, isBuiltin: boolean, doesImageExist: DoesImageExist) { - super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer") - this._path = path - this._isBuiltin = isBuiltin - this._doesImageExist = doesImageExist + super("Doesn't change anything, but emits warnings and errors", [], "ValidateLayer"); + this._path = path; + this._isBuiltin = isBuiltin; + this._doesImageExist = doesImageExist; } convert( json: LayerConfigJson, context: string ): { result: LayerConfigJson; errors: string[]; warnings?: string[]; information?: string[] } { - const errors = [] - const warnings = [] - const information = [] - context = "While validating a layer: " + context + const errors = []; + const warnings = []; + const information = []; + context = "While validating a layer: " + context; if (typeof json === "string") { - errors.push(context + ": This layer hasn't been expanded: " + json) + errors.push(context + ": This layer hasn't been expanded: " + json); return { result: null, - errors, - } + errors + }; } if (json.source === "special") { @@ -761,7 +835,7 @@ export class ValidateLayer extends DesugaringStep { ": layer " + json.id + " uses 'special' as source.osmTags. However, this layer is not a priviliged layer" - ) + ); } } @@ -770,48 +844,48 @@ export class ValidateLayer extends DesugaringStep { errors.push( context + ": this layer does not have a title defined but it does have tagRenderings. Not having a title will disable the popups, resulting in an unclickable element. Please add a title. If not having a popup is intended and the tagrenderings need to be kept (e.g. in a library layer), set `title: null` to disable this error." - ) + ); } if (json.title === null) { information.push( context + ": title is `null`. This results in an element that cannot be clicked - even though tagRenderings is set." - ) + ); } } if (json["builtin"] !== undefined) { - errors.push(context + ": This layer hasn't been expanded: " + json) + errors.push(context + ": This layer hasn't been expanded: " + json); return { result: null, - errors, - } + errors + }; } if (json.minzoom > Constants.minZoomLevelToAddNewPoint) { ;(json.presets?.length > 0 ? errors : warnings).push( `At ${context}: minzoom is ${json.minzoom}, this should be at most ${Constants.minZoomLevelToAddNewPoint} as a preset is set. Why? Selecting the pin for a new item will zoom in to level before adding the point. Having a greater minzoom will hide the points, resulting in possible duplicates` - ) + ); } { // duplicate ids in tagrenderings check const duplicates = Utils.Dedup( Utils.Dupiclates(Utils.NoNull((json.tagRenderings ?? []).map((tr) => tr["id"]))) - ) + ); if (duplicates.length > 0) { - console.log(json.tagRenderings) + console.log(json.tagRenderings); errors.push( "At " + context + ": some tagrenderings have a duplicate id: " + duplicates.join(", ") - ) + ); } } if (json.deletion !== undefined && json.deletion instanceof DeleteConfig) { if (json.deletion.softDeletionTags === undefined) { - warnings.push("No soft-deletion tags in deletion block for layer " + json.id) + warnings.push("No soft-deletion tags in deletion block for layer " + json.id); } } @@ -823,8 +897,8 @@ export class ValidateLayer extends DesugaringStep { errors.push( "Layer " + json.id + - 'still uses the old \'overpassTags\'-format. Please use "source": {"osmTags": }\' instead of "overpassTags": (note: this isn\'t your fault, the custom theme generator still spits out the old format)' - ) + "still uses the old 'overpassTags'-format. Please use \"source\": {\"osmTags\": }' instead of \"overpassTags\": (note: this isn't your fault, the custom theme generator still spits out the old format)" + ); } const forbiddenTopLevel = [ "icon", @@ -835,8 +909,8 @@ export class ValidateLayer extends DesugaringStep { "width", "color", "colour", - "iconOverlays", - ] + "iconOverlays" + ]; for (const forbiddenKey of forbiddenTopLevel) { if (json[forbiddenKey] !== undefined) errors.push( @@ -845,7 +919,7 @@ export class ValidateLayer extends DesugaringStep { json.id + " still has a forbidden key " + forbiddenKey - ) + ); } if (json["hideUnderlayingFeaturesMinPercentage"] !== undefined) { errors.push( @@ -853,70 +927,70 @@ export class ValidateLayer extends DesugaringStep { ": layer " + json.id + " contains an old 'hideUnderlayingFeaturesMinPercentage'" - ) + ); } if ( json.isShown !== undefined && (json.isShown["render"] !== undefined || json.isShown["mappings"] !== undefined) ) { - warnings.push(context + " has a tagRendering as `isShown`") + warnings.push(context + " has a tagRendering as `isShown`"); } } if (this._isBuiltin) { // Check location of layer file - const expected: string = `assets/layers/${json.id}/${json.id}.json` + const expected: string = `assets/layers/${json.id}/${json.id}.json`; if (this._path != undefined && this._path.indexOf(expected) < 0) { errors.push( "Layer is in an incorrect place. The path is " + this._path + ", but expected " + expected - ) + ); } } if (this._isBuiltin) { // Check for correct IDs if (json.tagRenderings?.some((tr) => tr["id"] === "")) { - const emptyIndexes: number[] = [] + const emptyIndexes: number[] = []; for (let i = 0; i < json.tagRenderings.length; i++) { - const tagRendering = json.tagRenderings[i] + const tagRendering = json.tagRenderings[i]; if (tagRendering["id"] === "") { - emptyIndexes.push(i) + emptyIndexes.push(i); } } errors.push( `Some tagrendering-ids are empty or have an emtpy string; this is not allowed (at ${context}.tagRenderings.[${emptyIndexes.join( "," )}])` - ) + ); } const duplicateIds = Utils.Dupiclates( (json.tagRenderings ?? []) ?.map((f) => f["id"]) .filter((id) => id !== "questions") - ) + ); if (duplicateIds.length > 0 && !Utils.runningFromConsole) { errors.push( `Some tagRenderings have a duplicate id: ${duplicateIds} (at ${context}.tagRenderings)` - ) + ); } if (json.description === undefined) { if (typeof json.source === null) { - errors.push(context + ": A priviliged layer must have a description") + errors.push(context + ": A priviliged layer must have a description"); } else { - warnings.push(context + ": A builtin layer should have a description") + warnings.push(context + ": A builtin layer should have a description"); } } } if (json.filter) { - const r = new On("filter", new Each( new ValidateFilter())).convert(json, context) - warnings.push(...(r.warnings ?? [])) - errors.push(...(r.errors ?? [])) - information.push(...(r.information ?? [])) + const r = new On("filter", new Each(new ValidateFilter())).convert(json, context); + warnings.push(...(r.warnings ?? [])); + errors.push(...(r.errors ?? [])); + information.push(...(r.information ?? [])); } if (json.tagRenderings !== undefined) { @@ -924,45 +998,45 @@ export class ValidateLayer extends DesugaringStep { "tagRenderings", new Each( new ValidateTagRenderings(json, this._doesImageExist, { - noQuestionHintCheck: json["#"]?.indexOf("no-question-hint-check") >= 0, + noQuestionHintCheck: json["#"]?.indexOf("no-question-hint-check") >= 0 }) ) - ).convert(json, context) - warnings.push(...(r.warnings ?? [])) - errors.push(...(r.errors ?? [])) - information.push(...(r.information ?? [])) + ).convert(json, context); + warnings.push(...(r.warnings ?? [])); + errors.push(...(r.errors ?? [])); + information.push(...(r.information ?? [])); } { const hasCondition = json.mapRendering?.filter( (mr) => mr["icon"] !== undefined && mr["icon"]["condition"] !== undefined - ) + ); if (hasCondition?.length > 0) { errors.push( "At " + context + ":\n One or more icons in the mapRenderings have a condition set. Don't do this, as this will result in an invisible but clickable element. Use extra filters in the source instead. The offending mapRenderings are:\n" + JSON.stringify(hasCondition, null, " ") - ) + ); } } if (json.presets !== undefined) { if (typeof json.source === "string") { - throw "A special layer cannot have presets" + throw "A special layer cannot have presets"; } // Check that a preset will be picked up by the layer itself - const baseTags = TagUtils.Tag(json.source["osmTags"]) + const baseTags = TagUtils.Tag(json.source["osmTags"]); for (let i = 0; i < json.presets.length; i++) { - const preset = json.presets[i] + const preset = json.presets[i]; const tags: { k: string; v: string }[] = new And( preset.tags.map((t) => TagUtils.Tag(t)) - ).asChange({id: "node/-1"}) - const properties = {} + ).asChange({ id: "node/-1" }); + const properties = {}; for (const tag of tags) { - properties[tag.k] = tag.v + properties[tag.k] = tag.v; } - const doMatch = baseTags.matchesProperties(properties) + const doMatch = baseTags.matchesProperties(properties); if (!doMatch) { errors.push( context + @@ -972,26 +1046,26 @@ export class ValidateLayer extends DesugaringStep { JSON.stringify(properties) + "\n The required tags are: " + baseTags.asHumanString(false, false, {}) - ) + ); } } } } catch (e) { - errors.push(e) + errors.push(e); } return { result: json, errors, warnings, - information, - } + information + }; } } export class ValidateFilter extends DesugaringStep { constructor() { - super("Detect common errors in the filters", [], "ValidateFilter") + super("Detect common errors in the filters", [], "ValidateFilter"); } convert( @@ -1005,23 +1079,22 @@ export class ValidateFilter extends DesugaringStep { } { if (typeof filter === "string") { // Calling another filter, we skip - return {result: filter} + return { result: filter }; } - const errors = [] + const errors = []; for (const option of filter.options) { - for (let i = 0; i < option.fields?.length ?? 0; i++) { - const field = option.fields[i] - const type = field.type ?? "string" + const field = option.fields[i]; + const type = field.type ?? "string"; if (Validators.availableTypes.find((t) => t === type) === undefined) { const err = `Invalid filter: ${type} is not a valid textfield type (at ${context}.fields[${i}])\n\tTry one of ${Array.from( Validators.availableTypes - ).join(",")}` - errors.push(err) + ).join(",")}`; + errors.push(err); } } } - return {result: filter, errors} + return { result: filter, errors }; } } @@ -1034,7 +1107,7 @@ export class DetectDuplicateFilters extends DesugaringStep<{ "Tries to detect layers where a shared filter can be used (or where similar filters occur)", [], "DetectDuplicateFilters" - ) + ); } convert( @@ -1046,11 +1119,11 @@ export class DetectDuplicateFilters extends DesugaringStep<{ warnings?: string[] information?: string[] } { - const errors: string[] = [] - const warnings: string[] = [] - const information: string[] = [] + const errors: string[] = []; + const warnings: string[] = []; + const information: string[] = []; - const {layers, themes} = json + const { layers, themes } = json; const perOsmTag = new Map< string, { @@ -1058,24 +1131,24 @@ export class DetectDuplicateFilters extends DesugaringStep<{ layout: LayoutConfigJson | undefined filter: FilterConfigJson }[] - >() + >(); for (const layer of layers) { - this.addLayerFilters(layer, perOsmTag) + this.addLayerFilters(layer, perOsmTag); } for (const theme of themes) { if (theme.id === "personal") { - continue + continue; } for (const layer of theme.layers) { if (typeof layer === "string") { - continue + continue; } if (layer["builtin"] !== undefined) { - continue + continue; } - this.addLayerFilters(layer, perOsmTag, theme) + this.addLayerFilters(layer, perOsmTag, theme); } } @@ -1083,25 +1156,25 @@ export class DetectDuplicateFilters extends DesugaringStep<{ perOsmTag.forEach((value, key) => { if (value.length <= 1) { // Seen this key just once, it is unique - return + return; } - let msg = "Possible duplicate filter: " + key - for (const {filter, layer, layout} of value) { - let id = "" + let msg = "Possible duplicate filter: " + key; + for (const { filter, layer, layout } of value) { + let id = ""; if (layout !== undefined) { - id = layout.id + ":" + id = layout.id + ":"; } - msg += `\n - ${id}${layer.id}.${filter.id}` + msg += `\n - ${id}${layer.id}.${filter.id}`; } - warnings.push(msg) - }) + warnings.push(msg); + }); return { result: json, errors, warnings, - information, - } + information + }; } /** @@ -1120,33 +1193,33 @@ export class DetectDuplicateFilters extends DesugaringStep<{ layout?: LayoutConfigJson | undefined ): void { if (layer.filter === undefined || layer.filter === null) { - return + return; } if (layer.filter["sameAs"] !== undefined) { - return + return; } for (const filter of <(string | FilterConfigJson)[]>layer.filter) { if (typeof filter === "string") { - continue + continue; } if (filter["#"]?.indexOf("ignore-possible-duplicate") >= 0) { - continue + continue; } for (const option of filter.options) { if (option.osmTags === undefined) { - continue + continue; } - const key = JSON.stringify(option.osmTags) + const key = JSON.stringify(option.osmTags); if (!perOsmTag.has(key)) { - perOsmTag.set(key, []) + perOsmTag.set(key, []); } perOsmTag.get(key).push({ layer, filter, - layout, - }) + layout + }); } } } diff --git a/src/Models/ThemeConfig/LayerConfig.ts b/src/Models/ThemeConfig/LayerConfig.ts index 4bac005391..097fb4829c 100644 --- a/src/Models/ThemeConfig/LayerConfig.ts +++ b/src/Models/ThemeConfig/LayerConfig.ts @@ -495,9 +495,7 @@ export default class LayerConfig extends WithContextLoader { usingLayer = [ new Title("Themes using this layer", 4), new List( - (usedInThemes ?? []).map( - (id) => new Link(id, "https://mapcomplete.org/" + id) - ) + (usedInThemes ?? []).map((id) => new Link(id, "https://mapcomplete.org/" + id)) ), ] } diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 940056a99b..a29afdcc40 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -242,7 +242,7 @@ export default class TagRenderingConfig { if (txt === "") { throw context + " Rendering for language " + ln + " is empty" } - if (txt.indexOf("{" + this.freeform.key + "}") >= 0) { + if (txt.indexOf("{" + this.freeform.key + "}") >= 0 || txt.indexOf("&LBRACE" + this.freeform.key + "&RBRACE") ) { continue } if (txt.indexOf("{" + this.freeform.key + ":") >= 0) { diff --git a/src/UI/Base/SubtleLink.svelte b/src/UI/Base/SubtleLink.svelte index 401c34a249..5a3d9f5058 100644 --- a/src/UI/Base/SubtleLink.svelte +++ b/src/UI/Base/SubtleLink.svelte @@ -34,6 +34,7 @@ class={twMerge(options.extraClasses, "button text-ellipsis")} {href} target={newTab ? "_blank" : undefined} + rel={newTab ? "noopener" : undefined} > {#if imageUrl !== undefined} diff --git a/src/UI/Base/Table.ts b/src/UI/Base/Table.ts index e27dbff899..c400b9129e 100644 --- a/src/UI/Base/Table.ts +++ b/src/UI/Base/Table.ts @@ -29,7 +29,7 @@ export default class Table extends BaseUIElement { const header = Utils.NoNull(headerMarkdownParts).join(" | ") const headerSep = headerMarkdownParts.map((part) => "-".repeat(part.length + 2)).join(" | ") const table = this._contents - .map((row) => row.map((el) => el?.AsMarkdown()?.replace("|", "\\|") ?? " ").join(" | ")) + .map((row) => row.map((el) => el?.AsMarkdown()?.replaceAll("\\","\\\\")?.replaceAll("|", "\\|") ?? " ").join(" | ")) .join("\n") return "\n\n" + [header, headerSep, table, ""].join("\n") diff --git a/src/UI/BigComponents/ContactLink.svelte b/src/UI/BigComponents/ContactLink.svelte index f3ccac9e2f..7cebd2c0db 100644 --- a/src/UI/BigComponents/ContactLink.svelte +++ b/src/UI/BigComponents/ContactLink.svelte @@ -35,7 +35,7 @@ src={`https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/community_index/${resource.type}.svg`} />
- + {resource.resolved.name ?? resource.resolved.url} {resource.resolved?.description} diff --git a/src/UI/BigComponents/CopyrightPanel.ts b/src/UI/BigComponents/CopyrightPanel.ts index 988a802cab..d066a4d005 100644 --- a/src/UI/BigComponents/CopyrightPanel.ts +++ b/src/UI/BigComponents/CopyrightPanel.ts @@ -102,7 +102,7 @@ export default class CopyrightPanel extends Combine { let bgAttr: BaseUIElement | string = undefined if (attrText && attrUrl) { bgAttr = - "" + attrText + "" + "" + attrText + "" } else if (attrUrl) { bgAttr = attrUrl } else { diff --git a/src/UI/BigComponents/OpenJosm.ts b/src/UI/BigComponents/OpenJosm.ts index 352082a7cb..8f86f6d989 100644 --- a/src/UI/BigComponents/OpenJosm.ts +++ b/src/UI/BigComponents/OpenJosm.ts @@ -16,7 +16,7 @@ export class OpenJosm extends Combine { const josmState = new UIEventSource(undefined) // Reset after 15s - josmState.stabilized(15000).addCallbackD((_) => josmState.setData(undefined)) + josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined)) const stateIndication = new VariableUiElement( josmState.map((state) => { @@ -45,7 +45,7 @@ export class OpenJosm extends Combine { const josmLink = `http://127.0.0.1:8111/load_and_zoom?left=${left}&right=${right}&top=${top}&bottom=${bottom}` Utils.download(josmLink) .then((answer) => josmState.setData(answer.replace(/\n/g, "").trim())) - .catch((_) => josmState.setData("ERROR")) + .catch(() => josmState.setData("ERROR")) }) .SetClass("w-full"), undefined, diff --git a/src/UI/BigComponents/UserProfile.svelte b/src/UI/BigComponents/UserProfile.svelte index 5ade85a15d..5cb514a4d0 100644 --- a/src/UI/BigComponents/UserProfile.svelte +++ b/src/UI/BigComponents/UserProfile.svelte @@ -37,6 +37,7 @@ diff --git a/src/UI/DownloadFlow/DownloadPanel.svelte b/src/UI/DownloadFlow/DownloadPanel.svelte index c9b9129585..220f94c3db 100644 --- a/src/UI/DownloadFlow/DownloadPanel.svelte +++ b/src/UI/DownloadFlow/DownloadPanel.svelte @@ -81,7 +81,7 @@ mimetype="image/png" mainText={t.downloadAsPng} helperText={t.downloadAsPngHelper} - construct={(_) => state.mapProperties.exportAsPng(4)} + construct={() => state.mapProperties.exportAsPng(4)} />
diff --git a/src/UI/Image/ImageUploadFlow.ts b/src/UI/Image/ImageUploadFlow.ts index 250df538df..f5ce9772d0 100644 --- a/src/UI/Image/ImageUploadFlow.ts +++ b/src/UI/Image/ImageUploadFlow.ts @@ -73,7 +73,7 @@ export class ImageUploadFlow extends Toggle { ]).SetClass("w-full flex justify-center items-center") const licenseStore = state?.osmConnection?.GetPreference( - Constants.OsmPreferenceKeyPicturesLicense, + "pictures-license", "CC0" ) diff --git a/src/UI/InputElement/ValidatedInput.svelte b/src/UI/InputElement/ValidatedInput.svelte index 57b702e1d5..ffdf92a51c 100644 --- a/src/UI/InputElement/ValidatedInput.svelte +++ b/src/UI/InputElement/ValidatedInput.svelte @@ -9,7 +9,7 @@ import { Unit } from "../../Models/Unit" import UnitInput from "../Popup/UnitInput.svelte" - export let type: ValidatorType + export let type: ValidatorType export let feedback: UIEventSource | undefined = undefined export let getCountry: () => string | undefined export let placeholder: string | Translation | undefined diff --git a/src/UI/InputElement/Validator.ts b/src/UI/InputElement/Validator.ts index e63a98f94a..5368701d41 100644 --- a/src/UI/InputElement/Validator.ts +++ b/src/UI/InputElement/Validator.ts @@ -1,6 +1,6 @@ -import BaseUIElement from "../BaseUIElement"; -import { Translation } from "../i18n/Translation"; -import Translations from "../i18n/Translations"; +import BaseUIElement from "../BaseUIElement" +import { Translation } from "../i18n/Translation" +import Translations from "../i18n/Translations" /** * A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback. @@ -16,13 +16,21 @@ export abstract class Validator { /** * What HTML-inputmode to use */ - public readonly inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search' + public readonly inputmode?: + | "none" + | "text" + | "tel" + | "url" + | "email" + | "numeric" + | "decimal" + | "search" public readonly textArea: boolean constructor( name: string, explanation: string | BaseUIElement, - inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search', + inputmode?: "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search", textArea?: false | boolean ) { this.name = name diff --git a/src/UI/InputElement/Validators/FloatValidator.ts b/src/UI/InputElement/Validators/FloatValidator.ts index 2c3f6adbbc..639c250b46 100644 --- a/src/UI/InputElement/Validators/FloatValidator.ts +++ b/src/UI/InputElement/Validators/FloatValidator.ts @@ -1,7 +1,7 @@ import { Translation } from "../../i18n/Translation" import Translations from "../../i18n/Translations" import { Validator } from "../Validator" -import { ValidatorType } from "../Validators"; +import { ValidatorType } from "../Validators" export default class FloatValidator extends Validator { inputmode: "decimal" = "decimal" diff --git a/src/UI/Map/MapLibreAdaptor.ts b/src/UI/Map/MapLibreAdaptor.ts index 6193ee7ca9..75f2d54d4a 100644 --- a/src/UI/Map/MapLibreAdaptor.ts +++ b/src/UI/Map/MapLibreAdaptor.ts @@ -172,7 +172,8 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { tileSize: layer["tile-size"] ?? 256, minzoom: layer["min_zoom"] ?? 1, maxzoom: layer["max_zoom"] ?? 25, - // scheme: background["type"] === "tms" ? "tms" : "xyz", + // Bit of a hack, but seems to work + scheme: layer.url.includes("{-y}") ? "tms" : "xyz", } } @@ -203,6 +204,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap { "{width}": "" + size, "{height}": "" + size, "{zoom}": "{z}", + "{-y}": "{y}", } for (const key in toReplace) { diff --git a/src/UI/Popup/LinkableImage.svelte b/src/UI/Popup/LinkableImage.svelte new file mode 100644 index 0000000000..798bebb09b --- /dev/null +++ b/src/UI/Popup/LinkableImage.svelte @@ -0,0 +1,73 @@ + +
+ + {#if linkable} + + {/if} +
diff --git a/src/UI/Popup/NearbyImages.svelte b/src/UI/Popup/NearbyImages.svelte new file mode 100644 index 0000000000..30c886914e --- /dev/null +++ b/src/UI/Popup/NearbyImages.svelte @@ -0,0 +1,54 @@ + + +
+
+ +

+ +

+ +
+ {#if $images.length === 0} + + {:else} +
+ {#each $images as image (image.pictureUrl)} + + + + {/each} +
+ {/if} +
diff --git a/src/UI/Popup/NearbyImages.ts b/src/UI/Popup/NearbyImages.ts deleted file mode 100644 index 6ddc1ab6f5..0000000000 --- a/src/UI/Popup/NearbyImages.ts +++ /dev/null @@ -1,316 +0,0 @@ -import Combine from "../Base/Combine" -import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource" -import { SlideShow } from "../Image/SlideShow" -import { ClickableToggle } from "../Input/Toggle" -import Loading from "../Base/Loading" -import { AttributedImage } from "../Image/AttributedImage" -import AllImageProviders from "../../Logic/ImageProviders/AllImageProviders" -import Svg from "../../Svg" -import BaseUIElement from "../BaseUIElement" -import { InputElement } from "../Input/InputElement" -import { VariableUiElement } from "../Base/VariableUIElement" -import Translations from "../i18n/Translations" -import { Mapillary } from "../../Logic/ImageProviders/Mapillary" -import { SubtleButton } from "../Base/SubtleButton" -import { GeoOperations } from "../../Logic/GeoOperations" -import Lazy from "../Base/Lazy" -import P4C from "pic4carto" -import { IndexedFeatureSource } from "../../Logic/FeatureSource/FeatureSource" - -export interface P4CPicture { - pictureUrl: string - date?: number - coordinates: { lat: number; lng: number } - provider: "Mapillary" | string - author? - license? - detailsUrl?: string - direction? - osmTags?: object /*To copy straight into OSM!*/ - thumbUrl: string - details: { - isSpherical: boolean - } -} - -export interface NearbyImageOptions { - lon: number - lat: number - // Radius of the upstream search - searchRadius?: 500 | number - maxDaysOld?: 1095 | number - blacklist: Store<{ url: string }[]> - shownImagesCount?: UIEventSource - towardscenter?: UIEventSource - allowSpherical?: UIEventSource - // Radius of what is shown. Useless to select a value > searchRadius; defaults to searchRadius - shownRadius?: UIEventSource -} - -class ImagesInLoadedDataFetcher { - private indexedFeatures: IndexedFeatureSource - - constructor(indexedFeatures: IndexedFeatureSource) { - this.indexedFeatures = indexedFeatures - } - - public fetchAround(loc: { lon: number; lat: number; searchRadius?: number }): P4CPicture[] { - const foundImages: P4CPicture[] = [] - this.indexedFeatures.features.data.forEach((feature) => { - const props = feature.properties - const images = [] - if (props.image) { - images.push(props.image) - } - for (let i = 0; i < 10; i++) { - if (props["image:" + i]) { - images.push(props["image:" + i]) - } - } - if (images.length == 0) { - return - } - const centerpoint = GeoOperations.centerpointCoordinates(feature) - const d = GeoOperations.distanceBetween(centerpoint, [loc.lon, loc.lat]) - if (loc.searchRadius !== undefined && d > loc.searchRadius) { - return - } - for (const image of images) { - foundImages.push({ - pictureUrl: image, - thumbUrl: image, - coordinates: { lng: centerpoint[0], lat: centerpoint[1] }, - provider: "OpenStreetMap", - details: { - isSpherical: false, - }, - }) - } - }) - const cleaned: P4CPicture[] = [] - const seen = new Set() - for (const foundImage of foundImages) { - if (seen.has(foundImage.pictureUrl)) { - continue - } - seen.add(foundImage.pictureUrl) - cleaned.push(foundImage) - } - return cleaned - } -} - -export default class NearbyImages extends Lazy { - constructor(options: NearbyImageOptions, state?: IndexedFeatureSource) { - super(() => { - const t = Translations.t.image.nearbyPictures - const shownImages = options.shownImagesCount ?? new UIEventSource(25) - - const loadedPictures = NearbyImages.buildPictureFetcher(options, state) - - const loadMoreButton = new Combine([ - new SubtleButton(Svg.add_svg(), t.loadMore).onClick(() => { - shownImages.setData(shownImages.data + 25) - }), - ]).SetClass("flex flex-col justify-center") - - const imageElements = loadedPictures.map( - (imgs) => { - if (imgs === undefined) { - return [] - } - const elements = (imgs.images ?? []) - .slice(0, shownImages.data) - .map((i) => this.prepareElement(i)) - if (imgs.images !== undefined && elements.length < imgs.images.length) { - // We effectively sliced some items, so we can increase the count - elements.push(loadMoreButton) - } - return elements - }, - [shownImages] - ) - - return new VariableUiElement( - loadedPictures.map((loaded) => { - if (loaded?.images === undefined) { - return NearbyImages.NoImagesView(new Loading(t.loading)).SetClass( - "animate-pulse" - ) - } - const images = loaded.images - const beforeFilter = loaded?.beforeFilter - if (beforeFilter === 0) { - return NearbyImages.NoImagesView(t.nothingFound.SetClass("alert block")) - } else if (images.length === 0) { - const removeFiltersButton = new SubtleButton( - Svg.filter_disable_svg(), - t.removeFilters - ).onClick(() => { - options.shownRadius.setData(options.searchRadius) - options.allowSpherical.setData(true) - options.towardscenter.setData(false) - }) - - return NearbyImages.NoImagesView( - t.allFiltered.SetClass("font-bold"), - removeFiltersButton - ) - } - - return new SlideShow(imageElements) - }) - ) - }) - } - - private static NoImagesView(...elems: BaseUIElement[]) { - return new Combine(elems) - .SetClass("flex flex-col justify-center items-center bg-gray-200 mb-2 rounded-lg") - .SetStyle( - "height: calc( var(--image-carousel-height) - 0.5rem ) ; max-height: calc( var(--image-carousel-height) - 0.5rem );" - ) - } - - private static buildPictureFetcher(options: NearbyImageOptions, state?: IndexedFeatureSource) { - const picManager = new P4C.PicturesManager({}) - const searchRadius = options.searchRadius ?? 500 - - const nearbyImages = - state !== undefined ? new ImagesInLoadedDataFetcher(state).fetchAround(options) : [] - - return Stores.FromPromise( - picManager.startPicsRetrievalAround( - new P4C.LatLng(options.lat, options.lon), - options.searchRadius ?? 500, - { - mindate: - new Date().getTime() - - (options.maxDaysOld ?? 3 * 365) * 24 * 60 * 60 * 1000, - towardscenter: false, - } - ) - ).map( - (images) => { - if (images === undefined) { - return undefined - } - images = (images ?? []).concat(nearbyImages) - const blacklisted = options.blacklist?.data - images = images?.filter( - (i) => - !blacklisted?.some((notAllowed) => - Mapillary.sameUrl(i.pictureUrl, notAllowed.url) - ) - ) - - const beforeFilterCount = images.length - - if (!options?.allowSpherical?.data) { - images = images?.filter((i) => i.details.isSpherical !== true) - } - - const shownRadius = options?.shownRadius?.data ?? searchRadius - if (shownRadius !== searchRadius) { - images = images.filter((i) => { - const d = GeoOperations.distanceBetween( - [i.coordinates.lng, i.coordinates.lat], - [options.lon, options.lat] - ) - return d <= shownRadius - }) - } - if (options.towardscenter?.data) { - images = images.filter((i) => { - if (i.direction === undefined || isNaN(i.direction)) { - return false - } - const bearing = GeoOperations.bearing( - [i.coordinates.lng, i.coordinates.lat], - [options.lon, options.lat] - ) - const diff = Math.abs((i.direction - bearing) % 360) - return diff < 40 - }) - } - - images?.sort((a, b) => { - const distanceA = GeoOperations.distanceBetween( - [a.coordinates.lng, a.coordinates.lat], - [options.lon, options.lat] - ) - const distanceB = GeoOperations.distanceBetween( - [b.coordinates.lng, b.coordinates.lat], - [options.lon, options.lat] - ) - return distanceA - distanceB - }) - - return { images, beforeFilter: beforeFilterCount } - }, - [options.blacklist, options.allowSpherical, options.towardscenter, options.shownRadius] - ) - } - - protected prepareElement(info: P4CPicture): BaseUIElement { - const provider = AllImageProviders.byName(info.provider) - return new AttributedImage({ url: info.pictureUrl, provider }) - } - - private static asAttributedImage(info: P4CPicture): AttributedImage { - const provider = AllImageProviders.byName(info.provider) - return new AttributedImage({ url: info.thumbUrl, provider, date: new Date(info.date) }) - } - - protected asToggle(info: P4CPicture): ClickableToggle { - const imgNonSelected = NearbyImages.asAttributedImage(info) - const imageSelected = NearbyImages.asAttributedImage(info) - - const nonSelected = new Combine([imgNonSelected]).SetClass("relative block") - const hoveringCheckmark = new Combine([ - Svg.confirm_svg().SetClass("block w-24 h-24 -ml-12 -mt-12"), - ]).SetClass("absolute left-1/2 top-1/2 w-0") - const selected = new Combine([imageSelected, hoveringCheckmark]).SetClass("relative block") - - return new ClickableToggle(selected, nonSelected).SetClass("").ToggleOnClick() - } -} - -export class SelectOneNearbyImage extends NearbyImages implements InputElement { - private readonly value: UIEventSource - - constructor( - options: NearbyImageOptions & { value?: UIEventSource }, - state?: IndexedFeatureSource - ) { - super(options, state) - this.value = options.value ?? new UIEventSource(undefined) - } - - GetValue(): UIEventSource { - return this.value - } - - IsValid(t: P4CPicture): boolean { - return false - } - - protected prepareElement(info: P4CPicture): BaseUIElement { - const toggle = super.asToggle(info) - toggle.isEnabled.addCallback((enabled) => { - if (enabled) { - this.value.setData(info) - } else if (this.value.data === info) { - this.value.setData(undefined) - } - }) - - this.value.addCallback((inf) => { - if (inf !== info) { - toggle.isEnabled.setData(false) - } - }) - - return toggle - } -} diff --git a/src/UI/Popup/NearbyImagesCollapsed.svelte b/src/UI/Popup/NearbyImagesCollapsed.svelte new file mode 100644 index 0000000000..e1679bff93 --- /dev/null +++ b/src/UI/Popup/NearbyImagesCollapsed.svelte @@ -0,0 +1,36 @@ + + +{#if expanded} + + {expanded = false}}/> + +{:else} + +{/if} diff --git a/src/UI/Popup/SendEmail.svelte b/src/UI/Popup/SendEmail.svelte new file mode 100644 index 0000000000..32a04f5f16 --- /dev/null +++ b/src/UI/Popup/SendEmail.svelte @@ -0,0 +1,22 @@ + +
+ + {button_text} + diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index 25a5c2e364..ef3771175d 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -1,52 +1,56 @@ import Combine from "./Base/Combine" -import {FixedUiElement} from "./Base/FixedUiElement" +import { FixedUiElement } from "./Base/FixedUiElement" import BaseUIElement from "./BaseUIElement" import Title from "./Base/Title" import Table from "./Base/Table" -import {RenderingSpecification, SpecialVisualization, SpecialVisualizationState,} from "./SpecialVisualization" -import {HistogramViz} from "./Popup/HistogramViz" -import {MinimapViz} from "./Popup/MinimapViz" -import {ShareLinkViz} from "./Popup/ShareLinkViz" -import {UploadToOsmViz} from "./Popup/UploadToOsmViz" -import {MultiApplyViz} from "./Popup/MultiApplyViz" -import {AddNoteCommentViz} from "./Popup/AddNoteCommentViz" -import {PlantNetDetectionViz} from "./Popup/PlantNetDetectionViz" +import { + RenderingSpecification, + SpecialVisualization, + SpecialVisualizationState, +} from "./SpecialVisualization" +import { HistogramViz } from "./Popup/HistogramViz" +import { MinimapViz } from "./Popup/MinimapViz" +import { ShareLinkViz } from "./Popup/ShareLinkViz" +import { UploadToOsmViz } from "./Popup/UploadToOsmViz" +import { MultiApplyViz } from "./Popup/MultiApplyViz" +import { AddNoteCommentViz } from "./Popup/AddNoteCommentViz" +import { PlantNetDetectionViz } from "./Popup/PlantNetDetectionViz" import TagApplyButton from "./Popup/TagApplyButton" -import {CloseNoteButton} from "./Popup/CloseNoteButton" -import {MapillaryLinkVis} from "./Popup/MapillaryLinkVis" -import {Store, Stores, UIEventSource} from "../Logic/UIEventSource" +import { CloseNoteButton } from "./Popup/CloseNoteButton" +import { MapillaryLinkVis } from "./Popup/MapillaryLinkVis" +import { Store, Stores, UIEventSource } from "../Logic/UIEventSource" import AllTagsPanel from "./Popup/AllTagsPanel.svelte" import AllImageProviders from "../Logic/ImageProviders/AllImageProviders" -import {ImageCarousel} from "./Image/ImageCarousel" -import {ImageUploadFlow} from "./Image/ImageUploadFlow" -import {VariableUiElement} from "./Base/VariableUIElement" -import {Utils} from "../Utils" -import Wikidata, {WikidataResponse} from "../Logic/Web/Wikidata" -import {Translation} from "./i18n/Translation" +import { ImageCarousel } from "./Image/ImageCarousel" +import { ImageUploadFlow } from "./Image/ImageUploadFlow" +import { VariableUiElement } from "./Base/VariableUIElement" +import { Utils } from "../Utils" +import Wikidata, { WikidataResponse } from "../Logic/Web/Wikidata" +import { Translation } from "./i18n/Translation" import Translations from "./i18n/Translations" import ReviewForm from "./Reviews/ReviewForm" import ReviewElement from "./Reviews/ReviewElement" import OpeningHoursVisualization from "./OpeningHours/OpeningHoursVisualization" import LiveQueryHandler from "../Logic/Web/LiveQueryHandler" -import {SubtleButton} from "./Base/SubtleButton" +import { SubtleButton } from "./Base/SubtleButton" import Svg from "../Svg" import NoteCommentElement from "./Popup/NoteCommentElement" import ImgurUploader from "../Logic/ImageProviders/ImgurUploader" import FileSelectorButton from "./Input/FileSelectorButton" -import {LoginToggle} from "./Popup/LoginButton" +import { LoginToggle } from "./Popup/LoginButton" import Toggle from "./Input/Toggle" -import {SubstitutedTranslation} from "./SubstitutedTranslation" +import { SubstitutedTranslation } from "./SubstitutedTranslation" import List from "./Base/List" import StatisticsPanel from "./BigComponents/StatisticsPanel" import AutoApplyButton from "./Popup/AutoApplyButton" -import {LanguageElement} from "./Popup/LanguageElement" +import { LanguageElement } from "./Popup/LanguageElement" import FeatureReviews from "../Logic/Web/MangroveReviews" import Maproulette from "../Logic/Maproulette" import SvelteUIElement from "./Base/SvelteUIElement" -import {BBoxFeatureSourceForLayer} from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" +import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" import QuestionViz from "./Popup/QuestionViz" -import {Feature, Point} from "geojson" -import {GeoOperations} from "../Logic/GeoOperations" +import { Feature, Point } from "geojson" +import { GeoOperations } from "../Logic/GeoOperations" import CreateNewNote from "./Popup/CreateNewNote.svelte" import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" import UserProfile from "./BigComponents/UserProfile.svelte" @@ -54,152 +58,39 @@ import LanguagePicker from "./LanguagePicker" import Link from "./Base/Link" import LayerConfig from "../Models/ThemeConfig/LayerConfig" import TagRenderingConfig from "../Models/ThemeConfig/TagRenderingConfig" -import NearbyImages, {NearbyImageOptions, P4CPicture, SelectOneNearbyImage,} from "./Popup/NearbyImages" -import {Tag} from "../Logic/Tags/Tag" -import ChangeTagAction from "../Logic/Osm/Actions/ChangeTagAction" -import {And} from "../Logic/Tags/And" -import {SaveButton} from "./Popup/SaveButton" -import Lazy from "./Base/Lazy" -import {CheckBox} from "./Input/Checkboxes" -import Slider from "./Input/Slider" -import {OsmTags, WayId} from "../Models/OsmFeature" +import { OsmTags, WayId } from "../Models/OsmFeature" import MoveWizard from "./Popup/MoveWizard" import SplitRoadWizard from "./Popup/SplitRoadWizard" -import {ExportAsGpxViz} from "./Popup/ExportAsGpxViz" +import { ExportAsGpxViz } from "./Popup/ExportAsGpxViz" import WikipediaPanel from "./Wikipedia/WikipediaPanel.svelte" import TagRenderingEditable from "./Popup/TagRendering/TagRenderingEditable.svelte" -import {PointImportButtonViz} from "./Popup/ImportButtons/PointImportButtonViz" +import { PointImportButtonViz } from "./Popup/ImportButtons/PointImportButtonViz" import WayImportButtonViz from "./Popup/ImportButtons/WayImportButtonViz" import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonViz" import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte" -import {OpenJosm} from "./BigComponents/OpenJosm" +import { OpenJosm } from "./BigComponents/OpenJosm" import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte" -import FediverseValidator from "./InputElement/Validators/FediverseValidator"; +import FediverseValidator from "./InputElement/Validators/FediverseValidator" +import SendEmail from "./Popup/SendEmail.svelte" +import NearbyImages from "./Popup/NearbyImages.svelte" +import NearbyImagesCollapsed from "./Popup/NearbyImagesCollapsed.svelte" class NearbyImageVis implements SpecialVisualization { // Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests - args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [ - { - name: "mode", - defaultValue: "expandable", - doc: "Indicates how this component is initialized. Options are: \n\n- `open`: always show and load the pictures\n- `collapsable`: show the pictures, but a user can collapse them\n- `expandable`: shown by default; but a user can collapse them.", - }, - { - name: "mapillary", - defaultValue: "true", - doc: "If 'true', includes a link to mapillary on this location.", - }, - ] + args: { name: string; defaultValue?: string; doc: string; required?: boolean }[] = [] docs = "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature" funcName = "nearby_images" constr( state: SpecialVisualizationState, - tagSource: UIEventSource>, + tags: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig ): BaseUIElement { - const t = Translations.t.image.nearbyPictures - const mode: "open" | "expandable" | "collapsable" = args[0] const [lon, lat] = GeoOperations.centerpointCoordinates(feature) - const id: string = tagSource.data["id"] - const canBeEdited: boolean = !!id?.match("(node|way|relation)/-?[0-9]+") - const selectedImage = new UIEventSource(undefined) - - let saveButton: BaseUIElement = undefined - if (canBeEdited) { - const confirmText: BaseUIElement = new SubstitutedTranslation( - t.confirm, - tagSource, - state - ) - - const onSave = async () => { - console.log("Selected a picture...", selectedImage.data) - const osmTags = selectedImage.data.osmTags - const tags: Tag[] = [] - for (const key in osmTags) { - tags.push(new Tag(key, osmTags[key])) - } - await state?.changes?.applyAction( - new ChangeTagAction(id, new And(tags), tagSource.data, { - theme: state?.layout.id, - changeType: "link-image", - }) - ) - } - saveButton = new SaveButton(selectedImage, state, confirmText, t.noImageSelected) - .onClick(onSave) - .SetClass("flex justify-end") - } - - const nearby = new Lazy(() => { - const towardsCenter = new CheckBox(t.onlyTowards, false) - - const maxSearchRadius = 100 - const stepSize = 10 - const defaultValue = Math.floor(maxSearchRadius / (2 * stepSize)) * stepSize - const fromOsmPreferences = state?.osmConnection - ?.GetPreference("nearby-images-radius", "" + defaultValue) - .sync( - (s) => Number(s), - [], - (i) => "" + i - ) - const radiusValue = new UIEventSource(fromOsmPreferences.data) - radiusValue.addCallbackAndRunD((v) => fromOsmPreferences.setData(v)) - - const radius = new Slider(stepSize, maxSearchRadius, { - value: radiusValue, - step: 10, - }) - const alreadyInTheImage = AllImageProviders.LoadImagesFor(tagSource) - const options: NearbyImageOptions & { value } = { - lon, - lat, - searchRadius: maxSearchRadius, - shownRadius: radius.GetValue(), - value: selectedImage, - blacklist: alreadyInTheImage, - towardscenter: towardsCenter.GetValue(), - maxDaysOld: 365 * 3, - } - const slideshow = canBeEdited - ? new SelectOneNearbyImage(options, state.indexedFeatures) - : new NearbyImages(options, state.indexedFeatures) - const controls = new Combine([ - towardsCenter, - new Combine([ - new VariableUiElement( - radius.GetValue().map((radius) => t.withinRadius.Subs({radius})) - ), - radius, - ]).SetClass("flex justify-between"), - ]).SetClass("flex flex-col") - return new Combine([ - slideshow, - controls, - saveButton, - new MapillaryLinkVis().constr(state, tagSource, [], feature).SetClass("mt-6"), - ]) - }) - - let withEdit: BaseUIElement = nearby - if (canBeEdited) { - withEdit = new Combine([t.hasMatchingPicture, nearby]).SetClass("flex flex-col") - } - - if (mode === "open") { - return withEdit - } - const toggleState = new UIEventSource(mode === "collapsable") - return new Toggle( - new Combine([new Title(t.title), withEdit]), - new Title(t.browseNearby).onClick(() => toggleState.setData(true)), - toggleState - ) + return new SvelteUIElement(NearbyImagesCollapsed, { tags, state, lon, lat, feature, layer }) } } @@ -386,24 +277,24 @@ export default class SpecialVisualizations { viz.docs, viz.args.length > 0 ? new Table( - ["name", "default", "description"], - viz.args.map((arg) => { - let defaultArg = arg.defaultValue ?? "_undefined_" - if (defaultArg == "") { - defaultArg = "_empty string_" - } - return [arg.name, defaultArg, arg.doc] - }) - ) + ["name", "default", "description"], + viz.args.map((arg) => { + let defaultArg = arg.defaultValue ?? "_undefined_" + if (defaultArg == "") { + defaultArg = "_empty string_" + } + return [arg.name, defaultArg, arg.doc] + }) + ) : undefined, new Title("Example usage of " + viz.funcName, 4), new FixedUiElement( viz.example ?? - "`{" + - viz.funcName + - "(" + - viz.args.map((arg) => arg.defaultValue).join(",") + - ")}`" + "`{" + + viz.funcName + + "(" + + viz.args.map((arg) => arg.defaultValue).join(",") + + ")}`" ).SetClass("literal-code"), ]) } @@ -462,14 +353,14 @@ export default class SpecialVisualizations { s.structuredExamples === undefined ? [] : s.structuredExamples().map((e) => { - return s.constr( - state, - new UIEventSource>(e.feature.properties), - e.args, - e.feature, - undefined - ) - }) + return s.constr( + state, + new UIEventSource>(e.feature.properties), + e.args, + e.feature, + undefined + ) + }) return new Combine([new Title(s.funcName), s.docs, ...examples]) } @@ -484,7 +375,7 @@ export default class SpecialVisualizations { let [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(AddNewPoint, { state, - coordinate: {lon, lat}, + coordinate: { lon, lat }, }) }, }, @@ -603,7 +494,7 @@ export default class SpecialVisualizations { feature: Feature ): BaseUIElement { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) - return new SvelteUIElement(CreateNewNote, {state, coordinate: {lon, lat}}) + return new SvelteUIElement(CreateNewNote, { state, coordinate: { lon, lat } }) }, }, new CloseNoteButton(), @@ -680,7 +571,7 @@ export default class SpecialVisualizations { docs: "Prints all key-value pairs of the object - used for debugging", args: [], constr: (state, tags: UIEventSource) => - new SvelteUIElement(AllTagsPanel, {tags, state}), + new SvelteUIElement(AllTagsPanel, { tags, state }), }, { funcName: "image_carousel", @@ -1229,28 +1120,12 @@ export default class SpecialVisualizations { }, ], constr(__, tags, args) { - return new VariableUiElement( - tags.map((tags) => { - const [to, subject, body, button_text] = args.map((str) => - Utils.SubstituteKeys(str, tags) - ) - const url = - "mailto:" + - to + - "?subject=" + - encodeURIComponent(subject) + - "&body=" + - encodeURIComponent(body) - return new SubtleButton(Svg.envelope_svg(), button_text, { - url, - }) - }) - ) + return new SvelteUIElement(SendEmail, { args, tags }) }, }, { funcName: "link", - docs: "Construct a link. By using the 'special' visualisation notation, translation should be easier", + docs: "Construct a link. By using the 'special' visualisation notation, translations should be easier", args: [ { name: "text", @@ -1319,7 +1194,7 @@ export default class SpecialVisualizations { ], constr(state, featureTags, args) { const [key, tr] = args - const translation = new Translation({"*": tr}) + const translation = new Translation({ "*": tr }) return new VariableUiElement( featureTags.map((tags) => { const properties: object[] = JSON.parse(tags[key]) @@ -1340,29 +1215,46 @@ export default class SpecialVisualizations { { funcName: "fediverse_link", docs: "Converts a fediverse username or link into a clickable link", - args: [{ - name: "key", - doc: "The attribute-name containing the link", - required: true - }], - constr(state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], feature: Feature, layer: LayerConfig): BaseUIElement { + args: [ + { + name: "key", + doc: "The attribute-name containing the link", + required: true, + }, + ], + constr( + state: SpecialVisualizationState, + tagSource: UIEventSource>, + argument: string[], + feature: Feature, + layer: LayerConfig + ): BaseUIElement { const key = argument[0] const validator = new FediverseValidator() - return new VariableUiElement(tagSource.map(tags => tags[key]).map(fediAccount => { - fediAccount = validator.reformat(fediAccount) - const [_, username, host] = fediAccount.match(FediverseValidator.usernameAtServer) + return new VariableUiElement( + tagSource + .map((tags) => tags[key]) + .map((fediAccount) => { + fediAccount = validator.reformat(fediAccount) + const [_, username, host] = fediAccount.match( + FediverseValidator.usernameAtServer + ) - return new Link(fediAccount, "https://" + host + "/@" + username, true) - } - )) - } - } + return new Link( + fediAccount, + "https://" + host + "/@" + username, + true + ) + }) + ) + }, + }, ] specialVisualizations.push(new AutoApplyButton(specialVisualizations)) const invalid = specialVisualizations - .map((sp, i) => ({sp, i})) + .map((sp, i) => ({ sp, i })) .filter((sp) => sp.sp.funcName === undefined) if (invalid.length > 0) { throw ( diff --git a/src/UI/StylesheetTestGui.svelte b/src/UI/StylesheetTestGui.svelte index 9a0705c00b..4ad1430061 100644 --- a/src/UI/StylesheetTestGui.svelte +++ b/src/UI/StylesheetTestGui.svelte @@ -1,7 +1,7 @@
@@ -38,6 +38,13 @@ Main action (disabled) + + +