forked from MapComplete/MapComplete
Merge branch 'develop' into RobinLinde-patch-1
This commit is contained in:
commit
90591924b0
34 changed files with 1056 additions and 488 deletions
|
@ -343,6 +343,7 @@
|
|||
},
|
||||
"id": "Rock type (crag/rock/cliff only)"
|
||||
},
|
||||
"reviews",
|
||||
{
|
||||
"id": "default_climbing_questions",
|
||||
"builtin": [
|
||||
|
|
|
@ -129,6 +129,7 @@
|
|||
},
|
||||
"payment-options",
|
||||
"opening_hours",
|
||||
"reviews",
|
||||
{
|
||||
"id": "shoe_rental",
|
||||
"question": {
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
SPDX-FileCopyrightText: Unkown
|
||||
SPDX-License-Identifier: CC0
|
||||
SPDX-License-Identifier: CC0-1.0
|
|
@ -62,6 +62,11 @@
|
|||
"cs": "Pitná voda"
|
||||
}
|
||||
},
|
||||
"titleIcons": [
|
||||
"icons.defaults",
|
||||
"auto:type",
|
||||
"auto:seasonal"
|
||||
],
|
||||
"pointRendering": [
|
||||
{
|
||||
"iconBadges": [
|
||||
|
@ -74,6 +79,10 @@
|
|||
]
|
||||
},
|
||||
"then": "close:#c33"
|
||||
},
|
||||
{
|
||||
"if": "tourism=artwork",
|
||||
"then": "circle:white;./assets/layers/artwork/artwork.svg"
|
||||
}
|
||||
],
|
||||
"iconSize": "40,40",
|
||||
|
@ -149,7 +158,10 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "operational_status=",
|
||||
"addExtraTags": ["disused:amenity=","amenity=drinking_water"],
|
||||
"addExtraTags": [
|
||||
"disused:amenity=",
|
||||
"amenity=drinking_water"
|
||||
],
|
||||
"then": {
|
||||
"en": "This drinking water works",
|
||||
"nl": "Deze drinkwaterfontein werkt",
|
||||
|
@ -296,11 +308,134 @@
|
|||
{
|
||||
"if": "fee=yes",
|
||||
"then": {
|
||||
"en":"One needs to pay to use this drinking water point"
|
||||
"en": "One needs to pay to use this drinking water point"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "seasonal",
|
||||
"question": {
|
||||
"en": "Is this drinking water point available all year round?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "seasonal=no",
|
||||
"then": {
|
||||
"en": "This drinking water point is available all around the year"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "seasonal=summer",
|
||||
"then": {
|
||||
"en": "This drinking water point is only available in summer"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "seasonal=spring;summer;autumn",
|
||||
"icon": "./assets/layers/drinking_water/no_winter.svg",
|
||||
"then": {
|
||||
"en": "This drinking water point is closed during the winter"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"builtin": "opening_hours_24_7",
|
||||
"override": {
|
||||
"questionHint": {
|
||||
"en": "These are the opening hours if the drinking water fountain is operational."
|
||||
},
|
||||
"+mappings": [
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"seasonal!=no",
|
||||
{
|
||||
"or": [
|
||||
{
|
||||
"and": [
|
||||
"seasonal!~.*winter.*",
|
||||
"_now:date~....-(12|01|02)-.."
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"seasonal!~.*spring.*",
|
||||
"_now:date~....-(03|04|05)-.."
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"seasonal!~.*summer.*",
|
||||
"_now:date~....-(06|07|08)-.."
|
||||
]
|
||||
},
|
||||
{
|
||||
"and": [
|
||||
"seasonal!~.*autumn.*",
|
||||
"_now:date~....-(09|10|11)-.."
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"en": "This drinking water fountain is closed this season. As such, the opening hours are not shown."
|
||||
},
|
||||
"hideInAnswer": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bench-artwork",
|
||||
"question": {
|
||||
"en": "Does this drinking water fountain have an artistic element?",
|
||||
"nl": "Heeft dit drinkwaterpunt een geintegreerd kunstwerk?"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "tourism=artwork",
|
||||
"addExtraTags": [
|
||||
"not:tourism:artwork="
|
||||
],
|
||||
"then": {
|
||||
"en": "This drinking water point has an integrated artwork",
|
||||
"nl": "Dit drinkwaterpunt heeft een geintegreerd kunstwerk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "not:tourism:artwork=yes",
|
||||
"then": {
|
||||
"en": "This drinking water point does not have an integrated artwork",
|
||||
"nl": "Dit drinkwaterpunt heeft geen geïntegreerd kunstwerk"
|
||||
},
|
||||
"addExtraTags": [
|
||||
"tourism="
|
||||
]
|
||||
},
|
||||
{
|
||||
"if": "tourism=",
|
||||
"then": {
|
||||
"en": "This drinking water point <span class=\"subtle\">probably</span> doesn't have an integrated artwork",
|
||||
"nl": "Dit drinkwaterpunt heeft <span class=\"subtle\">waarschijnlijk</span> geen geïntegreerd kunstwerk"
|
||||
},
|
||||
"hideInAnswer": true
|
||||
}
|
||||
],
|
||||
"questionHint": {
|
||||
"en": "E.g. it has an integrated statue or other non-trivial, creative work",
|
||||
"nl": "Bijvoorbeeld een standbeeld of ander, niet-triviaal kunstwerk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"builtin": "artwork.*artwork-question",
|
||||
"override": {
|
||||
"condition": "tourism=artwork"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "render-closest-drinking-water",
|
||||
"render": {
|
||||
|
@ -368,7 +503,7 @@
|
|||
]
|
||||
},
|
||||
"then": {
|
||||
"en":"This is a historic, manual water pump where no drinking water can be found"
|
||||
"en": "This is a historic, manual water pump where no drinking water can be found"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -33,6 +33,14 @@
|
|||
"https://osoc.be/editions/2020/cyclofix"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "no_winter.svg",
|
||||
"license": "CC0-1.0",
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"sources": []
|
||||
},
|
||||
{
|
||||
"path": "tap.svg",
|
||||
"license": "CC0-1.0",
|
||||
|
@ -42,5 +50,13 @@
|
|||
"sources": [
|
||||
"https://commons.wikimedia.org/wiki/File:Water_DIN-style.svg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "winter.svg",
|
||||
"license": "CC0-1.0",
|
||||
"authors": [
|
||||
"Pieter Vander Vennet"
|
||||
],
|
||||
"sources": []
|
||||
}
|
||||
]
|
81
assets/layers/drinking_water/no_winter.svg
Normal file
81
assets/layers/drinking_water/no_winter.svg
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="svg"
|
||||
version="1.1"
|
||||
width="307.91855"
|
||||
height="343.46448"
|
||||
viewBox="0 0 307.91855 343.46448"
|
||||
sodipodi:docname="no_winter.svg"
|
||||
inkscape:version="1.3.1 (1:1.3.1+202311172155+91b66b0783)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.2257936"
|
||||
inkscape:cx="190.48884"
|
||||
inkscape:cy="271.25284"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="995"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg" />
|
||||
<g
|
||||
id="g1"
|
||||
transform="rotate(-0.2875813,162.85132,95.170788)">
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 5.008225,86.10653 302.13815,258.03113"
|
||||
id="path3"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 153.09023,0.048073 1.65028,343.280557"
|
||||
id="path3-7"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 302.54004,85.82932 5.2907,257.54738"
|
||||
id="path4"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 5.786561,200.08323 53.987051,25.55191 -3.26951,61.20155"
|
||||
id="path6" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 56.719332,59.041006 3.20013,59.642774 -55.391241,26.23225"
|
||||
id="path7" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 201.87442,31.964241 152.44006,65.486554 101.3842,31.580519"
|
||||
id="path8" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 203.26283,313.48917 -49.04644,-34.08735 -51.44157,33.31792"
|
||||
id="path9" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 300.19729,199.07429 -53.31225,26.93149 4.84196,61.09726"
|
||||
id="path10" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 251.37871,59.718988 -4.44648,59.562822 54.83066,27.38467"
|
||||
id="path11" />
|
||||
</g>
|
||||
<path
|
||||
style="fill:#000000;stroke:#e40000;stroke-width:25;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 23.115301,297.04602 290.36883,29.792506"
|
||||
id="path1" />
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
2
assets/layers/drinking_water/no_winter.svg.license
Normal file
2
assets/layers/drinking_water/no_winter.svg.license
Normal file
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: Pieter Vander Vennet
|
||||
SPDX-License-Identifier: CC0-1.0
|
73
assets/layers/drinking_water/winter.svg
Normal file
73
assets/layers/drinking_water/winter.svg
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="svg"
|
||||
version="1.1"
|
||||
width="307.54224"
|
||||
height="343.37671"
|
||||
viewBox="0 0 307.54224 343.37671"
|
||||
sodipodi:docname="no_winter.svg"
|
||||
inkscape:version="1.3.1 (1:1.3.1+202311172155+91b66b0783)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="1.2257936"
|
||||
inkscape:cx="190.48884"
|
||||
inkscape:cy="271.25285"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="995"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 5.008225,86.10653 302.13815,258.03113"
|
||||
id="path3"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 153.09023,0.048073 1.65028,343.280557"
|
||||
id="path3-7"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 302.54004,85.82932 5.2907,257.54738"
|
||||
id="path4"
|
||||
sodipodi:nodetypes="cc" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 5.786561,200.08323 53.987051,25.55191 -3.26951,61.20155"
|
||||
id="path6" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 56.719332,59.041006 3.20013,59.642774 -55.391241,26.23225"
|
||||
id="path7" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 201.87442,31.964241 152.44006,65.486554 101.3842,31.580519"
|
||||
id="path8" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 203.26283,313.48917 -49.04644,-34.08735 -51.44157,33.31792"
|
||||
id="path9" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 300.19729,199.07429 -53.31225,26.93149 4.84196,61.09726"
|
||||
id="path10" />
|
||||
<path
|
||||
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:20;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 251.37871,59.718988 -4.44648,59.562822 54.83066,27.38467"
|
||||
id="path11" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.9 KiB |
2
assets/layers/drinking_water/winter.svg.license
Normal file
2
assets/layers/drinking_water/winter.svg.license
Normal file
|
@ -0,0 +1,2 @@
|
|||
SPDX-FileCopyrightText: Pieter Vander Vennet
|
||||
SPDX-License-Identifier: CC0-1.0
|
|
@ -27,7 +27,7 @@
|
|||
{
|
||||
"#": "ignore-image-in-then",
|
||||
"if": "wikipedia=",
|
||||
"then": "<a href='https://www.wikidata.org/wiki/{wikidata}' target='_blank' rel='noopener'><img src='./assets/svg/wikidata.svg' alt='WD'/></a>"
|
||||
"then": "<a class='h-8' href='https://www.wikidata.org/wiki/{wikidata}' target='_blank' rel='noopener'><img src='./assets/svg/wikidata.svg' alt='WD'/></a>"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -459,8 +459,8 @@
|
|||
{
|
||||
"if": "surface=fine_gravel",
|
||||
"then": {
|
||||
"en":"The surface is <b>fine gravel</b>",
|
||||
"nl":"De ondergrond bestaat uit <b>grind</b>"
|
||||
"en": "The surface is <b>fine gravel</b>",
|
||||
"nl": "De ondergrond bestaat uit <b>grind</b>"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
|
@ -345,8 +345,7 @@
|
|||
"key": "access:description"
|
||||
}
|
||||
},
|
||||
"questions",
|
||||
"reviews"
|
||||
"questions"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -102,9 +102,10 @@
|
|||
"=pointRendering": [
|
||||
{
|
||||
"marker": [
|
||||
{"icon": "pin",
|
||||
"color":"#ba2792"}
|
||||
,
|
||||
{
|
||||
"icon": "pin",
|
||||
"color": "#ba2792"
|
||||
},
|
||||
{
|
||||
"icon": "./assets/themes/cyclofix/key.svg"
|
||||
}
|
||||
|
|
|
@ -542,7 +542,9 @@
|
|||
"leisure=garden",
|
||||
"garden:type=facade_garden"
|
||||
],
|
||||
"snapToLayer":["walls_and_buildings"],
|
||||
"snapToLayer": [
|
||||
"walls_and_buildings"
|
||||
],
|
||||
"title": {
|
||||
"nl": "een geveltuintje",
|
||||
"en": "a facade garden",
|
||||
|
|
|
@ -166,31 +166,31 @@
|
|||
{
|
||||
"if": "sidewalk:left|right=yes",
|
||||
"then": {
|
||||
"en": "There is a sidewalk on this side of the road",
|
||||
"de": "Es gibt einen Bürgersteig auf dieser Straßenseite",
|
||||
"da": "Der er et fortov på denne side af vejen",
|
||||
"nl": "Er is een stoep aan deze kant van de weg",
|
||||
"fr": "Il y a un trottoir de ce côté de la route",
|
||||
"ca": "Hi ha una vorera a aquest costat del carrer",
|
||||
"es": "Hay una acera en este lado de la calle",
|
||||
"cs": "Na této straně silnice je chodník",
|
||||
"it": "C'è un marciapiede su questo lato della strada",
|
||||
"pl": "Jest chodnik z boku drogi"
|
||||
"en": "Yes, there is a sidewalk on this side of the road",
|
||||
"de": "Ja, es gibt einen Bürgersteig auf dieser Straßenseite",
|
||||
"da": "Ja, der er et fortov på denne side af vejen",
|
||||
"nl": "Ja, er is een stoep aan deze kant van de weg",
|
||||
"fr": "Oui, il y a un trottoir de ce côté de la route",
|
||||
"ca": "Sí, hi ha una vorera a aquest costat del carrer",
|
||||
"es": "Sí, hay una acera en este lado de la calle",
|
||||
"cs": "Ano, na této straně silnice je chodník",
|
||||
"it": "Sì, c'è un marciapiede su questo lato della strada",
|
||||
"pl": "Tak, jest chodnik z boku drogi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "sidewalk:left|right=no",
|
||||
"then": {
|
||||
"en": "There is no sidewalk to walk on",
|
||||
"de": "Es gibt keinen Bürgersteig für Fußgänger",
|
||||
"da": "Der er ikke noget fortov at gå på",
|
||||
"nl": "Er is geen stoep om op te lopen",
|
||||
"fr": "Il n'y a pas de trottoir où marcher",
|
||||
"ca": "No hi ha vorera per la que caminar",
|
||||
"es": "No hay acera por la que caminar",
|
||||
"cs": "Není tu žádný chodník",
|
||||
"it": "Non c'è un marciapiede su cui camminare",
|
||||
"pl": "Nie ma chodnika, którym można chodzić"
|
||||
"en": "No, there is no sidewalk to walk on",
|
||||
"de": "Nein, es gibt keinen Bürgersteig für Fußgänger",
|
||||
"da": "Nej, der er ikke noget fortov at gå på",
|
||||
"nl": "Nee, er is geen stoep om op te lopen",
|
||||
"fr": "Non, il n'y a pas de trottoir où marcher",
|
||||
"ca": "No, no hi ha vorera per la que caminar",
|
||||
"es": "No, no hay acera por la que caminar",
|
||||
"cs": "Ne, není tu žádný chodník",
|
||||
"it": "No, non c'è un marciapiede su cui camminare",
|
||||
"pl": "Nie, nie ma chodnika, którym można chodzić"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -4259,6 +4259,21 @@
|
|||
"question": "Is this drinking water spot still operational?",
|
||||
"render": "The operational status is <i>{operational_status}</i>"
|
||||
},
|
||||
"bench-artwork": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This drinking water point has an integrated artwork"
|
||||
},
|
||||
"1": {
|
||||
"then": "This drinking water point does not have an integrated artwork"
|
||||
},
|
||||
"2": {
|
||||
"then": "This drinking water point <span class=\"subtle\">probably</span> doesn't have an integrated artwork"
|
||||
}
|
||||
},
|
||||
"question": "Does this drinking water fountain have an artistic element?",
|
||||
"questionHint": "E.g. it has an integrated statue or other non-trivial, creative work"
|
||||
},
|
||||
"fee": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
@ -4270,9 +4285,33 @@
|
|||
},
|
||||
"question": "Is this drinking water point free to use?"
|
||||
},
|
||||
"opening_hours_24_7": {
|
||||
"override": {
|
||||
"+mappings": {
|
||||
"0": {
|
||||
"then": "This drinking water fountain is closed this season. As such, the opening hours are not shown."
|
||||
}
|
||||
},
|
||||
"questionHint": "These are the opening hours if the drinking water fountain is operational."
|
||||
}
|
||||
},
|
||||
"render-closest-drinking-water": {
|
||||
"render": "<a href='#{_closest_other_drinking_water_id}'>There is another drinking water fountain at {_closest_other_drinking_water_distance} meters</a>"
|
||||
},
|
||||
"seasonal": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "This drinking water point is available all around the year"
|
||||
},
|
||||
"1": {
|
||||
"then": "This drinking water point is only available in summer"
|
||||
},
|
||||
"2": {
|
||||
"then": "This drinking water point is closed during the winter"
|
||||
}
|
||||
},
|
||||
"question": "Is this drinking water point available all year round?"
|
||||
},
|
||||
"type": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
|
|
|
@ -4092,6 +4092,21 @@
|
|||
"question": "Is deze drinkwaterkraan nog steeds werkende?",
|
||||
"render": "Deze waterkraan-status is <i>{operational_status}</i>"
|
||||
},
|
||||
"bench-artwork": {
|
||||
"mappings": {
|
||||
"0": {
|
||||
"then": "Dit drinkwaterpunt heeft een geintegreerd kunstwerk"
|
||||
},
|
||||
"1": {
|
||||
"then": "Dit drinkwaterpunt heeft geen geïntegreerd kunstwerk"
|
||||
},
|
||||
"2": {
|
||||
"then": "Dit drinkwaterpunt heeft <span class=\"subtle\">waarschijnlijk</span> geen geïntegreerd kunstwerk"
|
||||
}
|
||||
},
|
||||
"question": "Heeft dit drinkwaterpunt een geintegreerd kunstwerk?",
|
||||
"questionHint": "Bijvoorbeeld een standbeeld of ander, niet-triviaal kunstwerk"
|
||||
},
|
||||
"render-closest-drinking-water": {
|
||||
"render": "<a href='#{_closest_other_drinking_water_id}'>Er bevindt zich een ander drinkwaterpunt op {_closest_other_drinking_water_distance} meter</a>"
|
||||
},
|
||||
|
|
168
package-lock.json
generated
168
package-lock.json
generated
|
@ -92,7 +92,7 @@
|
|||
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||
"read-file": "^0.2.0",
|
||||
"sass": "^1.58.0",
|
||||
"sharp": "^0.30.5",
|
||||
"sharp": "^0.32.6",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-preprocess": "^5.0.1",
|
||||
|
@ -4704,6 +4704,12 @@
|
|||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/b4a": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
|
||||
"integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz",
|
||||
|
@ -5898,9 +5904,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
|
||||
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
@ -6868,6 +6874,12 @@
|
|||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"node_modules/fast-fifo": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
|
@ -9144,6 +9156,12 @@
|
|||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-html-parser": {
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.5.tgz",
|
||||
|
@ -10035,6 +10053,12 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"node_modules/queue-tick": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
|
||||
"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
|
@ -10679,23 +10703,23 @@
|
|||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.30.7",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz",
|
||||
"integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==",
|
||||
"version": "0.32.6",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz",
|
||||
"integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.1",
|
||||
"node-addon-api": "^5.0.0",
|
||||
"detect-libc": "^2.0.2",
|
||||
"node-addon-api": "^6.1.0",
|
||||
"prebuild-install": "^7.1.1",
|
||||
"semver": "^7.3.7",
|
||||
"semver": "^7.5.4",
|
||||
"simple-get": "^4.0.1",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tar-fs": "^3.0.4",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.13.0"
|
||||
"node": ">=14.15.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
|
@ -10744,12 +10768,6 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp/node_modules/node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sharp/node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
|
@ -10765,6 +10783,28 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp/node_modules/tar-fs": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
|
||||
"integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp/node_modules/tar-stream": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz",
|
||||
"integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"b4a": "^1.6.4",
|
||||
"fast-fifo": "^1.2.0",
|
||||
"streamx": "^2.15.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
@ -11113,6 +11153,16 @@
|
|||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/streamx": {
|
||||
"version": "2.15.5",
|
||||
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.5.tgz",
|
||||
"integrity": "sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fast-fifo": "^1.1.0",
|
||||
"queue-tick": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
|
@ -16879,6 +16929,12 @@
|
|||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"b4a": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
|
||||
"integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-polyfill-corejs2": {
|
||||
"version": "0.1.10",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz",
|
||||
|
@ -17748,9 +17804,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz",
|
||||
"integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
|
||||
"integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
|
||||
"dev": true
|
||||
},
|
||||
"detective": {
|
||||
|
@ -18485,6 +18541,12 @@
|
|||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||
},
|
||||
"fast-fifo": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
|
||||
"integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
|
||||
"dev": true
|
||||
},
|
||||
"fast-glob": {
|
||||
"version": "3.2.12",
|
||||
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
|
||||
|
@ -20209,6 +20271,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
|
||||
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
|
||||
"dev": true
|
||||
},
|
||||
"node-html-parser": {
|
||||
"version": "6.1.5",
|
||||
"resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.5.tgz",
|
||||
|
@ -20786,6 +20854,12 @@
|
|||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="
|
||||
},
|
||||
"queue-tick": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz",
|
||||
"integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
|
||||
"dev": true
|
||||
},
|
||||
"quick-lru": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
|
||||
|
@ -21290,18 +21364,18 @@
|
|||
}
|
||||
},
|
||||
"sharp": {
|
||||
"version": "0.30.7",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.7.tgz",
|
||||
"integrity": "sha512-G+MY2YW33jgflKPTXXptVO28HvNOo9G3j0MybYAHeEmby+QuD2U98dT6ueht9cv/XDqZspSpIhoSW+BAKJ7Hig==",
|
||||
"version": "0.32.6",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz",
|
||||
"integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.1",
|
||||
"node-addon-api": "^5.0.0",
|
||||
"detect-libc": "^2.0.2",
|
||||
"node-addon-api": "^6.1.0",
|
||||
"prebuild-install": "^7.1.1",
|
||||
"semver": "^7.3.7",
|
||||
"semver": "^7.5.4",
|
||||
"simple-get": "^4.0.1",
|
||||
"tar-fs": "^2.1.1",
|
||||
"tar-fs": "^3.0.4",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -21339,12 +21413,6 @@
|
|||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||
"dev": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
|
@ -21354,6 +21422,28 @@
|
|||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"tar-fs": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz",
|
||||
"integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"mkdirp-classic": "^0.5.2",
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
}
|
||||
},
|
||||
"tar-stream": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz",
|
||||
"integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"b4a": "^1.6.4",
|
||||
"fast-fifo": "^1.2.0",
|
||||
"streamx": "^2.15.0"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
@ -21606,6 +21696,16 @@
|
|||
"resolved": "https://registry.npmjs.org/store/-/store-2.0.12.tgz",
|
||||
"integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw=="
|
||||
},
|
||||
"streamx": {
|
||||
"version": "2.15.5",
|
||||
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.5.tgz",
|
||||
"integrity": "sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-fifo": "^1.1.0",
|
||||
"queue-tick": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
"generate:schemas": "ts2json-schema -p src/Models/ThemeConfig/Json/ -o Docs/Schemas/ -t tsconfig.json -R . -m \".*ConfigJson\" && echo 'tsjson is done' && vite-node scripts/fixSchemas.ts ",
|
||||
"fix:schemas": "vite-node scripts/fixSchemas.ts ",
|
||||
"watch:schemas": "cd Models/ThemeConfig/Json & ls | entr -s 'npm run generate:schemas' ",
|
||||
"generate:service-worker": "tsc src/service-worker.ts --outFile public/service-worker.js && git_hash=$(git rev-parse HEAD) && sed -i \"s/GITHUB-COMMIT/$git_hash/\" public/service-worker.js",
|
||||
"generate:service-worker": "tsc src/service-worker.ts --outFile public/service-worker.js && git_hash=$(git rev-parse HEAD) && sed -i.bak \"s/GITHUB-COMMIT/$git_hash/\" public/service-worker.js && rm public/service-worker.js.bak",
|
||||
"optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'",
|
||||
"generate:stats": "vite-node scripts/GenerateSeries.ts",
|
||||
"reset:layeroverview": "mkdir -p ./src/assets/generated/layers; echo {\\\"themes\\\":[]} > ./src/assets/generated/known_themes.json && echo {\\\"layers\\\": []} > ./src/assets/generated/known_layers.json && rm -f ./src/assets/generated/layers/*.json && rm -f ./src/assets/generated/themes/*.json && cp ./assets/layers/usersettings/usersettings.json ./src/assets/generated/layers/usersettings.json && npm run generate:layeroverview && vite-node scripts/generateLayerOverview.ts -- --force",
|
||||
|
@ -178,7 +178,7 @@
|
|||
"prettier-plugin-tailwindcss": "^0.3.0",
|
||||
"read-file": "^0.2.0",
|
||||
"sass": "^1.58.0",
|
||||
"sharp": "^0.30.5",
|
||||
"sharp": "^0.32.6",
|
||||
"svelte": "^3.55.1",
|
||||
"svelte-check": "^3.0.2",
|
||||
"svelte-preprocess": "^5.0.1",
|
||||
|
|
|
@ -841,6 +841,10 @@ video {
|
|||
margin-right: 3rem;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
@ -877,10 +881,6 @@ video {
|
|||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.ml-1 {
|
||||
margin-left: 0.25rem;
|
||||
}
|
||||
|
@ -929,6 +929,10 @@ video {
|
|||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.mt-12 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.mr-12 {
|
||||
margin-right: 3rem;
|
||||
}
|
||||
|
@ -1784,6 +1788,10 @@ video {
|
|||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.pt-1 {
|
||||
padding-top: 0.25rem;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,36 @@ async function prepareFile(url: string): Promise<string> {
|
|||
}
|
||||
return null
|
||||
}
|
||||
async function handleDelete(req: http.IncomingMessage, res: ServerResponse) {
|
||||
let body = ""
|
||||
req.on("data", (chunk) => {
|
||||
body = body + chunk
|
||||
})
|
||||
const paths = req.url.split("/")
|
||||
console.log("Got a valid delete to:", paths.join("/"))
|
||||
for (let i = 1; i < paths.length; i++) {
|
||||
const p = paths.slice(0, i)
|
||||
const dir = STATIC_PATH + p.join("/")
|
||||
if (!fs.existsSync(dir)) {
|
||||
res.writeHead(304, { "Content-Type": MIME_TYPES.html })
|
||||
res.write("<html><body>No parent directory, nothing deleted</body></html>", "utf8")
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
}
|
||||
const path = STATIC_PATH + paths.join("/")
|
||||
if(!fs.existsSync(path)){
|
||||
res.writeHead(304, { "Content-Type": MIME_TYPES.html })
|
||||
res.write("<html><body>File not found</body></html>", "utf8")
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
|
||||
fs.renameSync(path, path+".bak")
|
||||
res.writeHead(200, { "Content-Type": MIME_TYPES.html })
|
||||
res.write("<html><body>File moved to backup</body></html>", "utf8")
|
||||
res.end()
|
||||
}
|
||||
|
||||
async function handlePost(req: http.IncomingMessage, res: ServerResponse) {
|
||||
let body = ""
|
||||
|
@ -53,6 +83,7 @@ async function handlePost(req: http.IncomingMessage, res: ServerResponse) {
|
|||
|
||||
await new Promise((resolve) => req.on("end", resolve))
|
||||
|
||||
console.log(new Date().toISOString())
|
||||
let parsed: any
|
||||
try {
|
||||
parsed = JSON.parse(body)
|
||||
|
@ -84,7 +115,7 @@ async function handlePost(req: http.IncomingMessage, res: ServerResponse) {
|
|||
|
||||
http.createServer(async (req: http.IncomingMessage, res) => {
|
||||
try {
|
||||
console.log(req.method + " " + req.url, "from:", req.headers.origin)
|
||||
console.log(req.method + " " + req.url, "from:", req.headers.origin, new Date().toISOString())
|
||||
res.setHeader(
|
||||
"Access-Control-Allow-Headers",
|
||||
"Origin, X-Requested-With, Content-Type, Accept"
|
||||
|
@ -101,6 +132,12 @@ http.createServer(async (req: http.IncomingMessage, res) => {
|
|||
return
|
||||
}
|
||||
|
||||
if(req.method === "DELETE"){
|
||||
console.log("Got a DELETE", new Date())
|
||||
await handleDelete(req, res)
|
||||
return
|
||||
}
|
||||
|
||||
const url = new URL(`http://127.0.0.1/` + req.url)
|
||||
console.log("URL pathname is")
|
||||
if (url.pathname.endsWith("overview")) {
|
||||
|
|
|
@ -1,42 +1,14 @@
|
|||
import { Utils } from "../../Utils"
|
||||
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
|
||||
export class ThemeMetaTagging {
|
||||
public static readonly themeName = "usersettings"
|
||||
public static readonly themeName = "usersettings"
|
||||
|
||||
public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
|
||||
feat.properties._description
|
||||
.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
|
||||
?.at(1)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_d",
|
||||
() => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.href.match(/mastodon|en.osm.town/) !== null
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
|
||||
((feat) => {
|
||||
const e = document.createElement("div")
|
||||
e.innerHTML = feat.properties._d
|
||||
return Array.from(e.getElementsByTagName("a")).filter(
|
||||
(a) => a.getAttribute("rel")?.indexOf("me") >= 0
|
||||
)[0]?.href
|
||||
})(feat)
|
||||
)
|
||||
Utils.AddLazyProperty(
|
||||
feat.properties,
|
||||
"_mastodon_candidate",
|
||||
() => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
|
||||
)
|
||||
feat.properties["__current_backgroun"] = "initial_value"
|
||||
}
|
||||
}
|
||||
public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
|
||||
Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
|
||||
Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
|
||||
feat.properties['__current_backgroun'] = 'initial_value'
|
||||
}
|
||||
}
|
|
@ -10,10 +10,7 @@ import {
|
|||
SetDefault,
|
||||
} from "./Conversion"
|
||||
import { LayerConfigJson } from "../Json/LayerConfigJson"
|
||||
import {
|
||||
MinimalTagRenderingConfigJson,
|
||||
TagRenderingConfigJson,
|
||||
} from "../Json/TagRenderingConfigJson"
|
||||
import { MinimalTagRenderingConfigJson, TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
|
||||
import { Utils } from "../../../Utils"
|
||||
import RewritableConfigJson from "../Json/RewritableConfigJson"
|
||||
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
|
||||
|
@ -26,7 +23,7 @@ import predifined_filters from "../../../../assets/layers/filters/filters.json"
|
|||
import { TagConfigJson } from "../Json/TagConfigJson"
|
||||
import PointRenderingConfigJson, { IconConfigJson } from "../Json/PointRenderingConfigJson"
|
||||
import ValidationUtils from "./ValidationUtils"
|
||||
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
|
||||
import { RenderingSpecification } from "../../../UI/SpecialVisualization"
|
||||
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
|
||||
import { ConfigMeta } from "../../../UI/Studio/configMeta"
|
||||
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
|
||||
|
@ -1220,6 +1217,54 @@ export class AddRatingBadge extends DesugaringStep<LayerConfigJson> {
|
|||
return json
|
||||
}
|
||||
}
|
||||
export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
|
||||
constructor() {
|
||||
super(
|
||||
"The auto-icon creates a (non-clickable) title icon based on a tagRendering which has icons",
|
||||
["titleIcons"],
|
||||
"AutoTitleIcon"
|
||||
)
|
||||
}
|
||||
|
||||
convert(json: LayerConfigJson, context: ConversionContext): LayerConfigJson {
|
||||
json = { ...json }
|
||||
json.titleIcons = [...json.titleIcons]
|
||||
for (let i = 0; i < json.titleIcons.length; i++) {
|
||||
const titleIcon = json.titleIcons[i]
|
||||
if (typeof titleIcon !== "string") {
|
||||
continue
|
||||
}
|
||||
if (!titleIcon.startsWith("auto:")) {
|
||||
continue
|
||||
}
|
||||
const trId = titleIcon.substring("auto:".length)
|
||||
const tr = <QuestionableTagRenderingConfigJson>json.tagRenderings.find((tr) => tr["id"] === trId)
|
||||
if (tr === undefined) {
|
||||
context
|
||||
.enters("titleIcons", i)
|
||||
.err("TagRendering with id " + trId + " not found")
|
||||
continue
|
||||
}
|
||||
const mappings: { if: TagConfigJson, then: string }[] = tr.mappings?.filter(m => m.icon !== undefined)
|
||||
.map(m => {
|
||||
const path: string = typeof m.icon === "string" ? m.icon : m.icon.path
|
||||
const img = `<img class="m-1 h-6 w-6 low-interaction rounded" src='${path}'/>`
|
||||
return ({ if: m.if, then: img })
|
||||
})
|
||||
if (mappings.length === 0) {
|
||||
context
|
||||
.enters("titleIcons", i)
|
||||
.warn("TagRendering with id " + trId + " does not have any icons, not generating an icon for this")
|
||||
continue
|
||||
}
|
||||
json.titleIcons[i] = <TagRenderingConfigJson>{
|
||||
id: "title_icon_auto_" + trId,
|
||||
mappings,
|
||||
}
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
export class PrepareLayer extends Fuse<LayerConfigJson> {
|
||||
constructor(state: DesugaringContext) {
|
||||
|
@ -1247,6 +1292,7 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
|
|||
),
|
||||
new SetDefault("titleIcons", ["icons.defaults"]),
|
||||
new AddRatingBadge(),
|
||||
new AutoTitleIcon(),
|
||||
new On(
|
||||
"titleIcons",
|
||||
(layer) =>
|
||||
|
|
|
@ -240,6 +240,8 @@ export interface LayerConfigJson {
|
|||
* Use an empty array to hide them.
|
||||
* Note that "defaults" will insert all the default titleIcons (which are added automatically)
|
||||
*
|
||||
* Use `auto:<tagrenderingId>` to automatically create an icon based on a tagRendering which has icons
|
||||
*
|
||||
* Type: icon[]
|
||||
* group: infobox
|
||||
*/
|
||||
|
|
|
@ -118,7 +118,7 @@ export default class MoreScreen extends Combine {
|
|||
if (search === undefined) {
|
||||
return true
|
||||
}
|
||||
search = search.toLocaleLowerCase()
|
||||
search = Utils.RemoveDiacritics(search.toLocaleLowerCase())
|
||||
if (search.length > 3 && layout.id.toLowerCase().indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ export default class MoreScreen extends Combine {
|
|||
continue
|
||||
}
|
||||
const term = entity["*"] ?? entity[Locale.language.data]
|
||||
if (term?.toLowerCase()?.indexOf(search) >= 0) {
|
||||
if (Utils.RemoveDiacritics(term?.toLowerCase())?.indexOf(search) >= 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
</script>
|
||||
|
||||
{#if mapping.icon !== undefined}
|
||||
<div class="inline-flex">
|
||||
<div class="inline-flex items-center">
|
||||
<img
|
||||
class={twJoin(`mapping-icon-${mapping.iconClass}`, "mr-1")}
|
||||
src={mapping.icon}
|
||||
|
|
|
@ -1,202 +1,200 @@
|
|||
<script lang="ts">
|
||||
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||
import FreeformInput from "./FreeformInput.svelte"
|
||||
import Translations from "../../i18n/Translations.js"
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { createEventDispatcher, onDestroy } from "svelte"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import SpecialTranslation from "./SpecialTranslation.svelte"
|
||||
import TagHint from "../TagHint.svelte"
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import SubtleButton from "../../Base/SubtleButton.svelte"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import Constants from "../../../Models/Constants"
|
||||
import { Unit } from "../../../Models/Unit"
|
||||
import UserRelatedState from "../../../Logic/State/UserRelatedState"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import Search from "../../../assets/svg/Search.svelte";
|
||||
import Login from "../../../assets/svg/Login.svelte";
|
||||
import { ImmutableStore, UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import type { Mapping } from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||
import FreeformInput from "./FreeformInput.svelte"
|
||||
import Translations from "../../i18n/Translations.js"
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import { createEventDispatcher, onDestroy } from "svelte"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import SpecialTranslation from "./SpecialTranslation.svelte"
|
||||
import TagHint from "../TagHint.svelte"
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import SubtleButton from "../../Base/SubtleButton.svelte"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import TagRenderingMappingInput from "./TagRenderingMappingInput.svelte"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import Constants from "../../../Models/Constants"
|
||||
import { Unit } from "../../../Models/Unit"
|
||||
import UserRelatedState from "../../../Logic/State/UserRelatedState"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import Search from "../../../assets/svg/Search.svelte"
|
||||
import Login from "../../../assets/svg/Login.svelte"
|
||||
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig | undefined
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
export let selectedElement: Feature
|
||||
export let state: SpecialVisualizationState
|
||||
export let layer: LayerConfig | undefined
|
||||
|
||||
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
|
||||
let feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
|
||||
|
||||
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
let unit: Unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
|
||||
// Will be bound if a freeform is available
|
||||
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
|
||||
let selectedMapping: number = undefined
|
||||
let checkedMappings: boolean[]
|
||||
// Will be bound if a freeform is available
|
||||
let freeformInput = new UIEventSource<string>(tags?.[config.freeform?.key])
|
||||
let selectedMapping: number = undefined
|
||||
let checkedMappings: boolean[]
|
||||
|
||||
/**
|
||||
* Prepares and fills the checkedMappings
|
||||
*/
|
||||
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) {
|
||||
mappings = confg.mappings?.filter((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
return !m.hideInAnswer
|
||||
}
|
||||
return !m.hideInAnswer.matchesProperties(tgs)
|
||||
})
|
||||
// We received a new config -> reinit
|
||||
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
/**
|
||||
* Prepares and fills the checkedMappings
|
||||
*/
|
||||
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig) {
|
||||
mappings = confg.mappings?.filter((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
return !m.hideInAnswer
|
||||
}
|
||||
return !m.hideInAnswer.matchesProperties(tgs)
|
||||
})
|
||||
// We received a new config -> reinit
|
||||
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
|
||||
if (
|
||||
confg.mappings?.length > 0 &&
|
||||
confg.multiAnswer &&
|
||||
(checkedMappings === undefined ||
|
||||
checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
|
||||
) {
|
||||
const seenFreeforms = []
|
||||
TagUtils.FlattenMultiAnswer()
|
||||
checkedMappings = [
|
||||
...confg.mappings.map((mapping) => {
|
||||
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)
|
||||
if (matches && confg.freeform) {
|
||||
const newProps = TagUtils.changeAsProperties(mapping.if.asChange())
|
||||
seenFreeforms.push(newProps[confg.freeform.key])
|
||||
}
|
||||
return matches
|
||||
}),
|
||||
]
|
||||
if (
|
||||
confg.mappings?.length > 0 &&
|
||||
confg.multiAnswer &&
|
||||
(checkedMappings === undefined ||
|
||||
checkedMappings?.length < confg.mappings.length + (confg.freeform ? 1 : 0))
|
||||
) {
|
||||
const seenFreeforms = []
|
||||
TagUtils.FlattenMultiAnswer()
|
||||
checkedMappings = [
|
||||
...confg.mappings.map((mapping) => {
|
||||
const matches = TagUtils.MatchesMultiAnswer(mapping.if, tgs)
|
||||
if (matches && confg.freeform) {
|
||||
const newProps = TagUtils.changeAsProperties(mapping.if.asChange())
|
||||
seenFreeforms.push(newProps[confg.freeform.key])
|
||||
}
|
||||
return matches
|
||||
}),
|
||||
]
|
||||
|
||||
if (tgs !== undefined && confg.freeform) {
|
||||
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? []
|
||||
for (const seenFreeform of seenFreeforms) {
|
||||
if (!seenFreeform) {
|
||||
continue
|
||||
}
|
||||
const index = unseenFreeformValues.indexOf(seenFreeform)
|
||||
if (index < 0) {
|
||||
continue
|
||||
}
|
||||
unseenFreeformValues.splice(index, 1)
|
||||
if (tgs !== undefined && confg.freeform) {
|
||||
const unseenFreeformValues = tgs[confg.freeform.key]?.split(";") ?? []
|
||||
for (const seenFreeform of seenFreeforms) {
|
||||
if (!seenFreeform) {
|
||||
continue
|
||||
}
|
||||
const index = unseenFreeformValues.indexOf(seenFreeform)
|
||||
if (index < 0) {
|
||||
continue
|
||||
}
|
||||
unseenFreeformValues.splice(index, 1)
|
||||
}
|
||||
// TODO this has _to much_ values
|
||||
freeformInput.setData(unseenFreeformValues.join(";"))
|
||||
checkedMappings.push(unseenFreeformValues.length > 0)
|
||||
}
|
||||
}
|
||||
// TODO this has _to much_ values
|
||||
freeformInput.setData(unseenFreeformValues.join(";"))
|
||||
checkedMappings.push(unseenFreeformValues.length > 0)
|
||||
}
|
||||
}
|
||||
if (confg.freeform?.key) {
|
||||
if (!confg.multiAnswer) {
|
||||
// Somehow, setting multi-answer freeform values is broken if this is not set
|
||||
freeformInput.setData(tgs[confg.freeform.key])
|
||||
}
|
||||
} else {
|
||||
freeformInput.setData(undefined)
|
||||
}
|
||||
feedback.setData(undefined)
|
||||
}
|
||||
|
||||
$: {
|
||||
// Even though 'config' is not declared as a store, Svelte uses it as one to update the component
|
||||
// We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
|
||||
initialize($tags, config)
|
||||
}
|
||||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
||||
$: {
|
||||
try {
|
||||
selectedTags = config?.constructChangeSpecification(
|
||||
$freeformInput,
|
||||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data
|
||||
)
|
||||
} catch (e) {
|
||||
console.error("Could not calculate changeSpecification:", e)
|
||||
selectedTags = undefined
|
||||
}
|
||||
}
|
||||
|
||||
let dispatch = createEventDispatcher<{
|
||||
saved: {
|
||||
config: TagRenderingConfig
|
||||
applied: TagsFilter
|
||||
}
|
||||
}>()
|
||||
|
||||
function onSave() {
|
||||
if (selectedTags === undefined) {
|
||||
console.log("SelectedTags is undefined, ignoring 'onSave'-event")
|
||||
return
|
||||
}
|
||||
if (layer === undefined || layer?.source === null) {
|
||||
/**
|
||||
* This is a special, priviliged layer.
|
||||
* We simply apply the tags onto the records
|
||||
*/
|
||||
const kv = selectedTags.asChange(tags.data)
|
||||
for (const { k, v } of kv) {
|
||||
if (v === undefined || v === "") {
|
||||
delete tags.data[k]
|
||||
if (confg.freeform?.key) {
|
||||
if (!confg.multiAnswer) {
|
||||
// Somehow, setting multi-answer freeform values is broken if this is not set
|
||||
freeformInput.setData(tgs[confg.freeform.key])
|
||||
}
|
||||
} else {
|
||||
tags.data[k] = v
|
||||
freeformInput.setData(undefined)
|
||||
}
|
||||
}
|
||||
tags.ping()
|
||||
return
|
||||
feedback.setData(undefined)
|
||||
}
|
||||
|
||||
dispatch("saved", { config, applied: selectedTags })
|
||||
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
|
||||
theme: state.layout.id,
|
||||
changeType: "answer",
|
||||
})
|
||||
freeformInput.setData(undefined)
|
||||
selectedMapping = undefined
|
||||
selectedTags = undefined
|
||||
$: {
|
||||
// Even though 'config' is not declared as a store, Svelte uses it as one to update the component
|
||||
// We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
|
||||
initialize($tags, config)
|
||||
}
|
||||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
change
|
||||
.CreateChangeDescriptions()
|
||||
.then((changes) => state.changes.applyChanges(changes))
|
||||
.catch(console.error)
|
||||
}
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
||||
let featureSwitchIsTesting = state?.featureSwitchIsTesting ?? new ImmutableStore(false)
|
||||
let featureSwitchIsDebugging =
|
||||
state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false)
|
||||
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined)
|
||||
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0
|
||||
let question = config.question
|
||||
$: question = config.question
|
||||
if (state?.osmConnection) {
|
||||
onDestroy(
|
||||
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
|
||||
numberOfCs = ud.csCount
|
||||
})
|
||||
)
|
||||
}
|
||||
$: {
|
||||
try {
|
||||
selectedTags = config?.constructChangeSpecification(
|
||||
$freeformInput,
|
||||
selectedMapping,
|
||||
checkedMappings,
|
||||
tags.data,
|
||||
)
|
||||
} catch (e) {
|
||||
console.error("Could not calculate changeSpecification:", e)
|
||||
selectedTags = undefined
|
||||
}
|
||||
}
|
||||
|
||||
let dispatch = createEventDispatcher<{
|
||||
saved: {
|
||||
config: TagRenderingConfig
|
||||
applied: TagsFilter
|
||||
}
|
||||
}>()
|
||||
|
||||
function onSave() {
|
||||
if (selectedTags === undefined) {
|
||||
console.log("SelectedTags is undefined, ignoring 'onSave'-event")
|
||||
return
|
||||
}
|
||||
if (layer === undefined || layer?.source === null) {
|
||||
/**
|
||||
* This is a special, priviliged layer.
|
||||
* We simply apply the tags onto the records
|
||||
*/
|
||||
const kv = selectedTags.asChange(tags.data)
|
||||
for (const { k, v } of kv) {
|
||||
if (v === undefined || v === "") {
|
||||
delete tags.data[k]
|
||||
} else {
|
||||
tags.data[k] = v
|
||||
}
|
||||
}
|
||||
tags.ping()
|
||||
return
|
||||
}
|
||||
|
||||
dispatch("saved", { config, applied: selectedTags })
|
||||
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
|
||||
theme: state.layout.id,
|
||||
changeType: "answer",
|
||||
})
|
||||
freeformInput.setData(undefined)
|
||||
selectedMapping = undefined
|
||||
selectedTags = undefined
|
||||
|
||||
change
|
||||
.CreateChangeDescriptions()
|
||||
.then((changes) => state.changes.applyChanges(changes))
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
let featureSwitchIsTesting = state?.featureSwitchIsTesting ?? new ImmutableStore(false)
|
||||
let featureSwitchIsDebugging =
|
||||
state?.featureSwitches?.featureSwitchIsDebugging ?? new ImmutableStore(false)
|
||||
let showTags = state?.userRelatedState?.showTags ?? new ImmutableStore(undefined)
|
||||
let numberOfCs = state?.osmConnection?.userDetails?.data?.csCount ?? 0
|
||||
let question = config.question
|
||||
$: question = config.question
|
||||
if (state?.osmConnection) {
|
||||
onDestroy(
|
||||
state.osmConnection?.userDetails?.addCallbackAndRun((ud) => {
|
||||
numberOfCs = ud.csCount
|
||||
}),
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if question !== undefined}
|
||||
<div
|
||||
class="interactive border-interactive relative flex flex-col overflow-y-auto p-1 px-2"
|
||||
style="max-height: 85vh"
|
||||
class="interactive border-interactive relative flex flex-col overflow-y-auto px-2"
|
||||
style="max-height: 75vh"
|
||||
>
|
||||
<div class="sticky top-0" style="z-index: 11">
|
||||
<div class="interactive sticky top-0 flex justify-between">
|
||||
<div class="sticky top-0 interactive pt-1 flex justify-between" style="z-index: 11">
|
||||
<span class="font-bold">
|
||||
<SpecialTranslation t={question} {tags} {state} {layer} feature={selectedElement} />
|
||||
</span>
|
||||
<slot name="upper-right" />
|
||||
</div>
|
||||
<slot name="upper-right" />
|
||||
</div>
|
||||
|
||||
{#if config.questionhint}
|
||||
|
@ -213,7 +211,7 @@
|
|||
|
||||
{#if config.mappings?.length >= 8}
|
||||
<div class="sticky flex w-full">
|
||||
<Search class="h-6 w-6"/>
|
||||
<Search class="h-6 w-6" />
|
||||
<input type="text" bind:value={$searchTerm} class="w-full" />
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import { Utils } from "../../Utils"
|
||||
import type { ConversionMessage } from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import ErrorIndicatorForRegion from "./ErrorIndicatorForRegion.svelte"
|
||||
import { ChevronRightIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { ChevronRightIcon, TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import SchemaBasedInput from "./SchemaBasedInput.svelte"
|
||||
import FloatOver from "../Base/FloatOver.svelte"
|
||||
import TagRenderingInput from "./TagRenderingInput.svelte"
|
||||
|
@ -21,6 +21,7 @@
|
|||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
||||
|
||||
export let state: EditLayerState
|
||||
export let backToStudio: () => void
|
||||
let messages = state.messages
|
||||
let hasErrors = messages.mapD(
|
||||
(m: ConversionMessage[]) => m.filter((m) => m.level === "error").length
|
||||
|
@ -72,6 +73,10 @@
|
|||
})
|
||||
|
||||
let highlightedItem: UIEventSource<HighlightedTagRendering> = state.highlightedItem
|
||||
function deleteLayer() {
|
||||
state.delete()
|
||||
backToStudio()
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-screen flex-col">
|
||||
|
@ -113,6 +118,12 @@
|
|||
</div>
|
||||
<div class="flex flex-col" slot="content0">
|
||||
<Region {state} configs={perRegion["Basic"]} />
|
||||
<div class="mt-12">
|
||||
|
||||
<button on:click={() => deleteLayer()} class="small" >
|
||||
<TrashIcon class="h-6 w-6"/> Delete this layer
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div slot="title1" class="flex">
|
||||
|
|
|
@ -107,6 +107,9 @@ export abstract class EditJsonState<T> {
|
|||
return entry
|
||||
}
|
||||
|
||||
public async delete(){
|
||||
await this.server.delete(this.getId().data, this.category)
|
||||
}
|
||||
public getStoreFor<T>(path: ReadonlyArray<string | number>): UIEventSource<T | undefined> {
|
||||
const key = path.join(".")
|
||||
|
||||
|
|
|
@ -58,7 +58,14 @@ export default class StudioServer {
|
|||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
async delete(id: string, category: "layers" | "themes") {
|
||||
if (id === undefined || id === "") {
|
||||
return
|
||||
}
|
||||
await fetch(this.urlFor(id, category), {
|
||||
method: "DELETE"
|
||||
})
|
||||
}
|
||||
async update(id: string, config: string, category: "layers" | "themes") {
|
||||
if (id === undefined || id === "") {
|
||||
return
|
||||
|
|
|
@ -260,7 +260,7 @@
|
|||
<Loading />
|
||||
</div>
|
||||
{:else if state === "editing_layer"}
|
||||
<EditLayer state={editLayerState}>
|
||||
<EditLayer state={editLayerState} backToStudio={() => {state = undefined}}>
|
||||
<BackButton
|
||||
clss="small p-1"
|
||||
imageClass="w-8 h-8"
|
||||
|
@ -284,22 +284,23 @@
|
|||
</BackButton>
|
||||
</EditTheme>
|
||||
{/if}
|
||||
</LoginToggle>
|
||||
</If>
|
||||
|
||||
{#if { intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
{#if { intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
<FloatOver
|
||||
on:close={() => {
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
>
|
||||
<div class="flex h-full p-4 pr-12">
|
||||
<Walkthrough
|
||||
pages={{ intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
on:done={() => {
|
||||
>
|
||||
<div class="flex h-full p-4 pr-12">
|
||||
<Walkthrough
|
||||
pages={{ intro, tagrenderings: intro_tagrenderings }[$showIntro]?.sections}
|
||||
on:done={() => {
|
||||
showIntro.setData("no")
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</FloatOver>
|
||||
{/if}
|
||||
/>
|
||||
</div>
|
||||
</FloatOver>
|
||||
{/if}
|
||||
|
||||
</LoginToggle>
|
||||
</If>
|
||||
|
|
|
@ -66,71 +66,70 @@
|
|||
import OpenJosm from "./Base/OpenJosm.svelte"
|
||||
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
let layout = state.layout
|
||||
|
||||
let maplibremap: UIEventSource<MlMap> = state.map
|
||||
let selectedElement: UIEventSource<Feature> = state.selectedElement
|
||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
|
||||
let maplibremap: UIEventSource<MlMap> = state.map
|
||||
let selectedElement: UIEventSource<Feature> = state.selectedElement
|
||||
let selectedLayer: UIEventSource<LayerConfig> = state.selectedLayer
|
||||
|
||||
let currentZoom = state.mapProperties.zoom
|
||||
let showCrosshair = state.userRelatedState.showCrosshair
|
||||
let arrowKeysWereUsed = state.mapProperties.lastKeyNavigation
|
||||
let centerFeatures = state.closestFeatures.features
|
||||
$: console.log("Centerfeatures are", $centerFeatures)
|
||||
const selectedElementView = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined
|
||||
}
|
||||
let currentZoom = state.mapProperties.zoom
|
||||
let showCrosshair = state.userRelatedState.showCrosshair
|
||||
let arrowKeysWereUsed = state.mapProperties.lastKeyNavigation
|
||||
let centerFeatures = state.closestFeatures.features
|
||||
const selectedElementView = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (!(layer.tagRenderings?.length > 0) || layer.title === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementView, {
|
||||
state,
|
||||
layer,
|
||||
selectedElement,
|
||||
tags,
|
||||
}).SetClass("h-full w-full")
|
||||
},
|
||||
[selectedLayer]
|
||||
)
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementView, {
|
||||
state,
|
||||
layer,
|
||||
selectedElement,
|
||||
tags,
|
||||
}).SetClass("h-full w-full")
|
||||
},
|
||||
[selectedLayer],
|
||||
)
|
||||
|
||||
const selectedElementTitle = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined
|
||||
}
|
||||
const selectedElementTitle = selectedElement.map(
|
||||
(selectedElement) => {
|
||||
// Svelte doesn't properly reload some of the legacy UI-elements
|
||||
// As such, we _reconstruct_ the selectedElementView every time a new feature is selected
|
||||
// This is a bit wasteful, but until everything is a svelte-component, this should do the trick
|
||||
const layer = selectedLayer.data
|
||||
if (selectedElement === undefined || layer === undefined) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
|
||||
},
|
||||
[selectedLayer]
|
||||
)
|
||||
const tags = state.featureProperties.getStore(selectedElement.properties.id)
|
||||
return new SvelteUIElement(SelectedElementTitle, { state, layer, selectedElement, tags })
|
||||
},
|
||||
[selectedLayer],
|
||||
)
|
||||
|
||||
let mapproperties: MapProperties = state.mapProperties
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
||||
let availableLayers = state.availableLayers
|
||||
let userdetails = state.osmConnection.userDetails
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
let rasterLayerName =
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
|
||||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name
|
||||
})
|
||||
)
|
||||
let mapproperties: MapProperties = state.mapProperties
|
||||
let featureSwitches: FeatureSwitchState = state.featureSwitches
|
||||
let availableLayers = state.availableLayers
|
||||
let userdetails = state.osmConnection.userDetails
|
||||
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
|
||||
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
|
||||
let rasterLayerName =
|
||||
rasterLayer.data?.properties?.name ?? AvailableRasterLayers.maptilerDefaultLayer.properties.name
|
||||
onDestroy(
|
||||
rasterLayer.addCallbackAndRunD((l) => {
|
||||
rasterLayerName = l.properties.name
|
||||
}),
|
||||
)
|
||||
</script>
|
||||
|
||||
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
|
||||
|
@ -214,7 +213,7 @@
|
|||
<!-- bottom left elements -->
|
||||
<If condition={state.featureSwitches.featureSwitchFilter}>
|
||||
<MapControlButton on:click={() => state.guistate.openFilterView()}>
|
||||
<Filter class="h-6 w-6"/>
|
||||
<Filter class="h-6 w-6" />
|
||||
</MapControlButton>
|
||||
</If>
|
||||
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
|
||||
|
@ -231,16 +230,17 @@
|
|||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $arrowKeysWereUsed !== undefined}
|
||||
<div class="interactive pointer-events-auto p-1">
|
||||
{#each $centerFeatures as feat, i (feat.properties.id)}
|
||||
<div class="flex">
|
||||
<b>{i + 1}.</b>
|
||||
<Summary {state} feature={feat} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{#if $centerFeatures.length > 0}
|
||||
<div class="interactive pointer-events-auto p-1">
|
||||
{#each $centerFeatures as feat, i (feat.properties.id)}
|
||||
<div class="flex">
|
||||
<b>{i + 1}.</b>
|
||||
<Summary {state} feature={feat} />
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
<div class="flex flex-col items-end">
|
||||
<!-- bottom right elements -->
|
||||
|
@ -257,7 +257,7 @@
|
|||
<Plus class="w-8 h-8" />
|
||||
</MapControlButton>
|
||||
<MapControlButton on:click={() => mapproperties.zoom.update((z) => z - 1)}>
|
||||
<Min class="w-8 h-8"/>
|
||||
<Min class="w-8 h-8" />
|
||||
</MapControlButton>
|
||||
<If condition={featureSwitches.featureSwitchGeolocation}>
|
||||
<MapControlButton>
|
||||
|
@ -350,7 +350,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex" slot="title1">
|
||||
<Filter class="w-4 h-4"/>
|
||||
<Filter class="w-4 h-4" />
|
||||
<Tr t={Translations.t.general.menu.filter} />
|
||||
</div>
|
||||
|
||||
|
@ -374,7 +374,7 @@
|
|||
|
||||
<div class="flex" slot="title2">
|
||||
<If condition={state.featureSwitches.featureSwitchEnableExport}>
|
||||
<Download class="w-4 h-4"/>
|
||||
<Download class="w-4 h-4" />
|
||||
<Tr t={Translations.t.general.download.title} />
|
||||
</If>
|
||||
</div>
|
||||
|
@ -389,7 +389,7 @@
|
|||
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3" />
|
||||
|
||||
<div class="flex" slot="title4">
|
||||
<Share class="w-4 h-4"/>
|
||||
<Share class="w-4 h-4" />
|
||||
<Tr t={Translations.t.general.sharescreen.title} />
|
||||
</div>
|
||||
<div class="m-2" slot="content4">
|
||||
|
@ -441,12 +441,12 @@
|
|||
<Tr t={Translations.t.general.aboutMapComplete.intro} />
|
||||
|
||||
<a class="flex" href={Utils.HomepageLink()}>
|
||||
<Add class="h-6 w-6"/>
|
||||
<Add class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.backToIndex} />
|
||||
</a>
|
||||
|
||||
<a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank">
|
||||
<Bug class="h-6 w-6"/>
|
||||
<Bug class="h-6 w-6" />
|
||||
<Tr t={Translations.t.general.attribution.openIssueTracker} />
|
||||
</a>
|
||||
|
||||
|
@ -495,7 +495,7 @@
|
|||
</div>
|
||||
|
||||
<div class="flex" slot="title2">
|
||||
<Community class="w-6 h-6"/>
|
||||
<Community class="w-6 h-6" />
|
||||
<Tr t={Translations.t.communityIndex.title} />
|
||||
</div>
|
||||
<div class="m-2" slot="content2">
|
||||
|
@ -513,7 +513,7 @@
|
|||
<div class="m-2 flex flex-col" slot="content4">
|
||||
<If condition={featureSwitches.featureSwitchEnableLogin}>
|
||||
<OpenIdEditor mapProperties={state.mapProperties} />
|
||||
<OpenJosm {state}/>
|
||||
<OpenJosm {state} />
|
||||
<MapillaryLink mapProperties={state.mapProperties} />
|
||||
</If>
|
||||
|
||||
|
|
174
src/Utils.ts
174
src/Utils.ts
|
@ -11,7 +11,7 @@ export class Utils {
|
|||
public static readonly assets_path = "./assets/svg/"
|
||||
public static externalDownloadFunction: (
|
||||
url: string,
|
||||
headers?: any
|
||||
headers?: any,
|
||||
) => Promise<{ content: string } | { redirect: string }>
|
||||
public static Special_visualizations_tagsToApplyHelpText = `These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`.
|
||||
This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature.
|
||||
|
@ -150,7 +150,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
if (Utils.runningFromConsole) {
|
||||
return
|
||||
}
|
||||
DOMPurify.addHook("afterSanitizeAttributes", function (node) {
|
||||
DOMPurify.addHook("afterSanitizeAttributes", function(node) {
|
||||
// set all elements owning target to target=_blank + add noopener noreferrer
|
||||
const target = node.getAttribute("target")
|
||||
if (target) {
|
||||
|
@ -172,7 +172,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
*/
|
||||
public static ParseVisArgs(
|
||||
specs: { name: string; defaultValue?: string }[],
|
||||
args: string[]
|
||||
args: string[],
|
||||
): Record<string, string> {
|
||||
const parsed: Record<string, string> = {}
|
||||
if (args.length > specs.length) {
|
||||
|
@ -320,7 +320,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
object: any,
|
||||
name: string,
|
||||
init: () => any,
|
||||
whenDone?: () => void
|
||||
whenDone?: () => void,
|
||||
) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: false,
|
||||
|
@ -343,7 +343,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
object: any,
|
||||
name: string,
|
||||
init: () => Promise<any>,
|
||||
whenDone?: () => void
|
||||
whenDone?: () => void,
|
||||
) {
|
||||
Object.defineProperty(object, name, {
|
||||
enumerable: false,
|
||||
|
@ -483,7 +483,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
public static SubstituteKeys(
|
||||
txt: string | undefined,
|
||||
tags: Record<string, any> | undefined,
|
||||
useLang?: string
|
||||
useLang?: string,
|
||||
): string | undefined {
|
||||
if (txt === undefined) {
|
||||
return undefined
|
||||
|
@ -519,7 +519,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
"SubstituteKeys received a BaseUIElement to substitute in - this is probably a bug and will be downcast to a string\nThe key is",
|
||||
key,
|
||||
"\nThe value is",
|
||||
v
|
||||
v,
|
||||
)
|
||||
v = v.InnerConstructElement()?.textContent
|
||||
}
|
||||
|
@ -561,38 +561,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
target.push(...source)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively rewrites all keys from `+key`, `key+` and `=key` into `key
|
||||
*
|
||||
* Utils.CleanMergeObject({"condition":{"and+":["xyz"]}} // => {"condition":{"and":["xyz"]}}
|
||||
* @param obj
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
private static CleanMergeObject(obj: any) {
|
||||
if (Array.isArray(obj)) {
|
||||
const result = []
|
||||
for (const el of obj) {
|
||||
result.push(Utils.CleanMergeObject(el))
|
||||
}
|
||||
return result
|
||||
}
|
||||
if (typeof obj !== "object") {
|
||||
return obj
|
||||
}
|
||||
const newObj = {}
|
||||
for (let objKey in obj) {
|
||||
let cleanKey = objKey
|
||||
if (objKey.startsWith("+") || objKey.startsWith("=")) {
|
||||
cleanKey = objKey.substring(1)
|
||||
} else if (objKey.endsWith("+") || objKey.endsWith("=")) {
|
||||
cleanKey = objKey.substring(0, objKey.length - 1)
|
||||
}
|
||||
newObj[cleanKey] = Utils.CleanMergeObject(obj[objKey])
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all key-value pairs of the source into the target. This will change the target
|
||||
* If the key starts with a '+', the values of the list will be appended to the target instead of overwritten
|
||||
|
@ -664,7 +632,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
if (!Array.isArray(targetV)) {
|
||||
throw new Error(
|
||||
"Cannot concatenate: value to add is not an array: " +
|
||||
JSON.stringify(targetV)
|
||||
JSON.stringify(targetV),
|
||||
)
|
||||
}
|
||||
if (Array.isArray(sourceV)) {
|
||||
|
@ -672,9 +640,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
} else {
|
||||
throw new Error(
|
||||
"Could not merge concatenate " +
|
||||
JSON.stringify(sourceV) +
|
||||
" and " +
|
||||
JSON.stringify(targetV)
|
||||
JSON.stringify(sourceV) +
|
||||
" and " +
|
||||
JSON.stringify(targetV),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
@ -719,7 +687,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
path: string[],
|
||||
object: any,
|
||||
replaceLeaf: (leaf: any, travelledPath: string[]) => any,
|
||||
travelledPath: string[] = []
|
||||
travelledPath: string[] = [],
|
||||
): void {
|
||||
if (object == null) {
|
||||
return
|
||||
|
@ -750,7 +718,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
if (Array.isArray(sub)) {
|
||||
sub.forEach((el, i) =>
|
||||
Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, "" + i])
|
||||
Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, "" + i]),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
@ -767,7 +735,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
path: string[],
|
||||
object: any,
|
||||
collectedList: { leaf: any; path: string[] }[] = [],
|
||||
travelledPath: string[] = []
|
||||
travelledPath: string[] = [],
|
||||
): { leaf: any; path: string[] }[] {
|
||||
if (object === undefined || object === null) {
|
||||
return collectedList
|
||||
|
@ -797,7 +765,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
|
||||
if (Array.isArray(sub)) {
|
||||
sub.forEach((el, i) =>
|
||||
Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath, "" + i])
|
||||
Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath, "" + i]),
|
||||
)
|
||||
return collectedList
|
||||
}
|
||||
|
@ -841,7 +809,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
json: any,
|
||||
f: (v: object | number | string | boolean | undefined, path: string[]) => any,
|
||||
isLeaf: (object) => boolean = undefined,
|
||||
path: string[] = []
|
||||
path: string[] = [],
|
||||
) {
|
||||
if (json === undefined || json === null) {
|
||||
return f(json, path)
|
||||
|
@ -880,7 +848,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
json: any,
|
||||
collect: (v: number | string | boolean | undefined, path: string[]) => any,
|
||||
isLeaf: (object) => boolean = undefined,
|
||||
path = []
|
||||
path = [],
|
||||
): void {
|
||||
if (json === undefined) {
|
||||
return
|
||||
|
@ -955,7 +923,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
continue
|
||||
}
|
||||
const i = part.charCodeAt(0)
|
||||
result += '"' + keys[i] + '":' + part.substring(1)
|
||||
result += "\"" + keys[i] + "\":" + part.substring(1)
|
||||
}
|
||||
|
||||
return result
|
||||
|
@ -982,7 +950,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
url: string,
|
||||
headers?: any,
|
||||
method: "POST" | "GET" | "PUT" | "UPDATE" | "DELETE" | "OPTIONS" = "GET",
|
||||
content?: string
|
||||
content?: string,
|
||||
): Promise<
|
||||
| { content: string }
|
||||
| { redirect: string }
|
||||
|
@ -1047,7 +1015,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
public static async downloadJsonCached(
|
||||
url: string,
|
||||
maxCacheTimeMs: number,
|
||||
headers?: any
|
||||
headers?: any,
|
||||
): Promise<any> {
|
||||
const result = await Utils.downloadJsonAdvanced(url, headers)
|
||||
if (result["content"]) {
|
||||
|
@ -1059,7 +1027,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
public static async downloadJsonCachedAdvanced(
|
||||
url: string,
|
||||
maxCacheTimeMs: number,
|
||||
headers?: any
|
||||
headers?: any,
|
||||
): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> {
|
||||
const cached = Utils._download_cache.get(url)
|
||||
if (cached !== undefined) {
|
||||
|
@ -1069,9 +1037,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
const promise =
|
||||
/*NO AWAIT as we work with the promise directly */ Utils.downloadJsonAdvanced(
|
||||
url,
|
||||
headers
|
||||
)
|
||||
url,
|
||||
headers,
|
||||
)
|
||||
Utils._download_cache.set(url, { promise, timestamp: new Date().getTime() })
|
||||
return await promise
|
||||
}
|
||||
|
@ -1086,7 +1054,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
|
||||
public static async downloadJsonAdvanced(
|
||||
url: string,
|
||||
headers?: any
|
||||
headers?: any,
|
||||
): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> {
|
||||
const injected = Utils.injectedDownloads[url]
|
||||
if (injected !== undefined) {
|
||||
|
@ -1095,7 +1063,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
const result = await Utils.downloadAdvanced(
|
||||
url,
|
||||
Utils.Merge({ accept: "application/json" }, headers ?? {})
|
||||
Utils.Merge({ accept: "application/json" }, headers ?? {}),
|
||||
)
|
||||
if (result["error"] !== undefined) {
|
||||
return <{ error: string; url: string; statuscode?: number }>result
|
||||
|
@ -1115,7 +1083,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
"due to",
|
||||
e,
|
||||
"\n",
|
||||
e.stack
|
||||
e.stack,
|
||||
)
|
||||
return { error: "malformed", url }
|
||||
}
|
||||
|
@ -1136,7 +1104,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
| "{gpx=application/gpx+xml}"
|
||||
| "application/json"
|
||||
| "image/png"
|
||||
}
|
||||
},
|
||||
) {
|
||||
const element = document.createElement("a")
|
||||
let file
|
||||
|
@ -1240,7 +1208,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
e.preventDefault()
|
||||
return false
|
||||
},
|
||||
false
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1324,7 +1292,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
public static sortedByLevenshteinDistance<T>(
|
||||
reference: string,
|
||||
ts: T[],
|
||||
getName: (t: T) => string
|
||||
getName: (t: T) => string,
|
||||
): T[] {
|
||||
const withDistance: [T, number][] = ts.map((t) => [
|
||||
t,
|
||||
|
@ -1350,7 +1318,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
track[j][i] = Math.min(
|
||||
track[j][i - 1] + 1, // deletion
|
||||
track[j - 1][i] + 1, // insertion
|
||||
track[j - 1][i - 1] + indicator // substitution
|
||||
track[j - 1][i - 1] + indicator, // substitution
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1359,7 +1327,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
|
||||
public static MapToObj<V, T>(
|
||||
d: Map<string, V>,
|
||||
onValue: (t: V, key: string) => T
|
||||
onValue: (t: V, key: string) => T,
|
||||
): Record<string, T> {
|
||||
const o = {}
|
||||
const keys = Array.from(d.keys())
|
||||
|
@ -1376,7 +1344,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
* Utils.TransposeMap({"a" : ["b", "c"], "x" : ["b", "y"]}) // => {"b" : ["a", "x"], "c" : ["a"], "y" : ["x"]}
|
||||
*/
|
||||
public static TransposeMap<K extends string, V extends string>(
|
||||
d: Record<K, V[]>
|
||||
d: Record<K, V[]>,
|
||||
): Record<V, K[]> {
|
||||
const newD: Record<V, K[]> = <any>{}
|
||||
|
||||
|
@ -1450,7 +1418,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
|
||||
public static asDict(
|
||||
tags: { key: string; value: string | number }[]
|
||||
tags: { key: string; value: string | number }[],
|
||||
): Map<string, string | number> {
|
||||
const d = new Map<string, string | number>()
|
||||
|
||||
|
@ -1491,21 +1459,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
element.scrollIntoView({ behavior: "smooth", block: "nearest" })
|
||||
}
|
||||
|
||||
private static findParentWithScrolling(element: HTMLBaseElement): HTMLBaseElement {
|
||||
// Check if the element itself has scrolling
|
||||
if (element.scrollHeight > element.clientHeight) {
|
||||
return element
|
||||
}
|
||||
|
||||
// If the element does not have scrolling, check if it has a parent element
|
||||
if (!element.parentElement) {
|
||||
return null
|
||||
}
|
||||
|
||||
// If the element has a parent, repeat the process for the parent element
|
||||
return Utils.findParentWithScrolling(<HTMLBaseElement>element.parentElement)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the contents of `a` are the same (and in the same order) as `b`.
|
||||
* Might have false negatives in some cases
|
||||
|
@ -1572,7 +1525,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
*
|
||||
*/
|
||||
public static splitIntoSubstitutionParts(
|
||||
template: string
|
||||
template: string,
|
||||
): ({ message: string } | { subs: string })[] {
|
||||
const preparts = template.split("{")
|
||||
const spec: ({ message: string } | { subs: string })[] = []
|
||||
|
@ -1633,6 +1586,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
}
|
||||
|
||||
public static RemoveDiacritics(str?: string): string {
|
||||
if(!str){
|
||||
return str
|
||||
}
|
||||
return str.normalize("NFD").replace(/\p{Diacritic}/gu, "")
|
||||
}
|
||||
|
||||
public static randomString(length: number): string {
|
||||
let result = ""
|
||||
for (let i = 0; i < length; i++) {
|
||||
|
@ -1641,9 +1601,57 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively rewrites all keys from `+key`, `key+` and `=key` into `key
|
||||
*
|
||||
* Utils.CleanMergeObject({"condition":{"and+":["xyz"]}} // => {"condition":{"and":["xyz"]}}
|
||||
* @param obj
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
private static CleanMergeObject(obj: any) {
|
||||
if (Array.isArray(obj)) {
|
||||
const result = []
|
||||
for (const el of obj) {
|
||||
result.push(Utils.CleanMergeObject(el))
|
||||
}
|
||||
return result
|
||||
}
|
||||
if (typeof obj !== "object") {
|
||||
return obj
|
||||
}
|
||||
const newObj = {}
|
||||
for (let objKey in obj) {
|
||||
let cleanKey = objKey
|
||||
if (objKey.startsWith("+") || objKey.startsWith("=")) {
|
||||
cleanKey = objKey.substring(1)
|
||||
} else if (objKey.endsWith("+") || objKey.endsWith("=")) {
|
||||
cleanKey = objKey.substring(0, objKey.length - 1)
|
||||
}
|
||||
newObj[cleanKey] = Utils.CleanMergeObject(obj[objKey])
|
||||
}
|
||||
return newObj
|
||||
}
|
||||
|
||||
private static findParentWithScrolling(element: HTMLBaseElement): HTMLBaseElement {
|
||||
// Check if the element itself has scrolling
|
||||
if (element.scrollHeight > element.clientHeight) {
|
||||
return element
|
||||
}
|
||||
|
||||
// If the element does not have scrolling, check if it has a parent element
|
||||
if (!element.parentElement) {
|
||||
return null
|
||||
}
|
||||
|
||||
// If the element has a parent, repeat the process for the parent element
|
||||
return Utils.findParentWithScrolling(<HTMLBaseElement>element.parentElement)
|
||||
}
|
||||
|
||||
private static colorDiff(
|
||||
c0: { r: number; g: number; b: number },
|
||||
c1: { r: number; g: number; b: number }
|
||||
c1: { r: number; g: number; b: number },
|
||||
) {
|
||||
return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b)
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue