forked from MapComplete/MapComplete
Merge develop
This commit is contained in:
commit
04ecdad1bb
61 changed files with 702 additions and 705 deletions
|
@ -55,6 +55,28 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "accepts_debit_cards",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "payment:debit_cards=yes",
|
||||
"question": {
|
||||
"en": "Accepts debit cards"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "accepts_credit_cards",
|
||||
"options": [
|
||||
{
|
||||
"osmTags": "payment:credit_cards=yes",
|
||||
"question": {
|
||||
"en": "Accepts credit cards"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "has_image",
|
||||
"options": [
|
||||
|
|
|
@ -962,15 +962,41 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"id": "vegetarian",
|
||||
"id": "food-category",
|
||||
"options": [
|
||||
{
|
||||
"question": {
|
||||
"en": "Has a vegetarian menu",
|
||||
"nl": "Heeft een vegetarisch menu",
|
||||
"de": "Vegetarische Gerichte im Angebot",
|
||||
"es": "Tiene menú vegetariano",
|
||||
"fr": "A un menu végétarien"
|
||||
"fr": "A un menu végétarien",
|
||||
"nl": "Heeft een vegetarisch menu"
|
||||
}
|
||||
},
|
||||
{
|
||||
"question": {
|
||||
"en": "Only fastfood buisinesses"
|
||||
},
|
||||
"osmTags": "amenity=fast_food"
|
||||
},
|
||||
{
|
||||
"question": {
|
||||
"en": "Only restaurants"
|
||||
},
|
||||
"osmTags": "amenity=restaurant"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "vegetarian",
|
||||
"options": [
|
||||
{
|
||||
"question": {
|
||||
"en": "Has a vegan menu",
|
||||
"nl": "Heeft een veganistisch menu",
|
||||
"de": "Vegane Gerichte im Angebot",
|
||||
"es": "Tiene menú vegano",
|
||||
"fr": "A un menu végétalien"
|
||||
},
|
||||
"osmTags": {
|
||||
"or": [
|
||||
|
@ -988,11 +1014,12 @@
|
|||
"options": [
|
||||
{
|
||||
"question": {
|
||||
"en": "Has a vegan menu",
|
||||
"nl": "Heeft een veganistisch menu",
|
||||
"de": "Vegane Gerichte im Angebot",
|
||||
"es": "Tiene menú vegano",
|
||||
"fr": "A un menu végétalien"
|
||||
"en": "Has a halal menu",
|
||||
"nl": "Heeft een halal menu",
|
||||
"de": "Halal Gerichte im Angebot",
|
||||
"es": "Tiene menú halah",
|
||||
"fr": "A un menu halal",
|
||||
"da": "Har en halalmenu"
|
||||
},
|
||||
"osmTags": {
|
||||
"or": [
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
"website",
|
||||
"email",
|
||||
"phone",
|
||||
"mastodon",
|
||||
{
|
||||
"builtin": "opening_hours_24_7",
|
||||
"override": {
|
||||
|
|
|
@ -67,7 +67,19 @@
|
|||
"defaults"
|
||||
],
|
||||
"render": "<a href='tel:{phone}'><img textmode='📞' alt='phone' src='./assets/layers/questions/phone.svg'/></a>",
|
||||
"condition": "phone~*"
|
||||
"mappings": [
|
||||
{
|
||||
"#": "ignore-image-in-then",
|
||||
"if": "contact:phone~*",
|
||||
"then": "<a href='tel:{contact:phone}'><img textmode='📞' alt='phone' src='./assets/layers/questions/phone.svg'/></a>"
|
||||
}
|
||||
],
|
||||
"condition": {
|
||||
"or": [
|
||||
"phone~*",
|
||||
"contact:phone~*"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "emaillink",
|
||||
|
@ -75,7 +87,19 @@
|
|||
"defaults"
|
||||
],
|
||||
"render": "<a href='mailto:{email}'><img textmode='✉️' alt='email' src='./assets/layers/questions/send_email.svg'/></a>",
|
||||
"condition": "email~*"
|
||||
"mappings": [
|
||||
{
|
||||
"#": "ignore-image-in-then",
|
||||
"if": "contact:email~*",
|
||||
"then": "<a href='mailto:{contact:email}'><img textmode='✉️' alt='email' src='./assets/layers/questions/send_email.svg'/></a>"
|
||||
}
|
||||
],
|
||||
"condition": {
|
||||
"or": [
|
||||
"email~*",
|
||||
"contact:email~*"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "websitelink",
|
||||
|
|
|
@ -79,24 +79,6 @@
|
|||
"type": "pnat"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": {
|
||||
"and": [
|
||||
"highway=living_street",
|
||||
"_country!=be"
|
||||
]
|
||||
},
|
||||
"then": {
|
||||
"en": "This is a living street, which has a maxspeed of 20km/h",
|
||||
"de": "Dies ist eine Wohnstraße, auf der eine Höchstgeschwindigkeit von 20 km/h gilt",
|
||||
"nl": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h"
|
||||
},
|
||||
"icon": {
|
||||
"path": "./assets/layers/maxspeed/living_street_be.svg",
|
||||
"class": "large"
|
||||
},
|
||||
"hideInAnswer": true
|
||||
},
|
||||
{
|
||||
"if": "highway=living_street",
|
||||
"then": {
|
||||
|
@ -108,6 +90,9 @@
|
|||
"path": "./assets/layers/maxspeed/living_street_be.svg",
|
||||
"class": "large"
|
||||
},
|
||||
"addExtraTags": [
|
||||
"maxspeed=20"
|
||||
],
|
||||
"hideInAnswer": "_country!=be"
|
||||
}
|
||||
],
|
||||
|
|
|
@ -173,11 +173,13 @@
|
|||
"render": {
|
||||
"*": "<a href='tel:{phone}'>{phone}</a>"
|
||||
},
|
||||
"icon": "./assets/layers/questions/phone.svg",
|
||||
"mappings": [
|
||||
{
|
||||
"if": "contact:phone~*",
|
||||
"then": "<a href='tel:{contact:phone}'>{contact:phone}</a>",
|
||||
"hideInAnswer": true
|
||||
"hideInAnswer": true,
|
||||
"icon": "./assets/layers/questions/phone.svg"
|
||||
}
|
||||
],
|
||||
"freeform": {
|
||||
|
@ -188,6 +190,21 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "mastodon",
|
||||
"description": "Shows and asks for the mastodon handle",
|
||||
"question": {
|
||||
"en": "What is the Mastodon-handle of {title()}?"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "contact:mastodon",
|
||||
"type": "fediverse"
|
||||
},
|
||||
"render": {
|
||||
"*": "{fediverse_link(contact:mastodon)}"
|
||||
},
|
||||
"icon": "./assets/svg/mastodon.svg"
|
||||
},
|
||||
{
|
||||
"id": "osmlink",
|
||||
"render": {
|
||||
|
@ -205,6 +222,7 @@
|
|||
"render": {
|
||||
"*": "<a href='mailto:{email}' target='_blank'>{email}</a>"
|
||||
},
|
||||
"icon": "./assets/svg/envelope.svg",
|
||||
"labels": [
|
||||
"contact"
|
||||
],
|
||||
|
@ -236,6 +254,7 @@
|
|||
"mappings": [
|
||||
{
|
||||
"if": "contact:email~*",
|
||||
"icon": "./assets/svg/envelope.svg",
|
||||
"then": "<a href='mailto:{contact:email}' target='_blank'>{contact:email}</a>",
|
||||
"hideInAnswer": true
|
||||
}
|
||||
|
@ -253,6 +272,7 @@
|
|||
"labels": [
|
||||
"contact"
|
||||
],
|
||||
"icon": "./assets/layers/icons/website.svg",
|
||||
"question": {
|
||||
"en": "What is the website of {title()}?",
|
||||
"nl": "Wat is de website van {title()}?",
|
||||
|
@ -292,7 +312,8 @@
|
|||
{
|
||||
"if": "contact:website~*",
|
||||
"then": "<a href='{contact:website}' rel='nofollow noopener noreferrer' target='_blank'>{contact:website}</a>",
|
||||
"hideInAnswer": true
|
||||
"hideInAnswer": true,
|
||||
"icon": "./assets/layers/icons/website.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2174,6 +2195,33 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "check_date",
|
||||
"question": {
|
||||
"en": "When was this object last checked?",
|
||||
"de": "Wann wurde dieses Objekt zuletzt kontrolliert?",
|
||||
"nl": "Wanneer is dit object voor het laatst gecontroleerd?"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "check_date",
|
||||
"type": "date"
|
||||
},
|
||||
"render": {
|
||||
"en": "This object was last checked on <b>{check_date}</b>",
|
||||
"de": "Dieses Objekt wurde zuletzt kontrolliert am <b>{check_date}</b>",
|
||||
"nl": "Dit object is voor het laatst gecontroleerd op <b>{check_date}</b>"
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"if": "check_date:={_now:date}",
|
||||
"then": {
|
||||
"en": "This object was last checked today",
|
||||
"de": "Dieses Objekt wurde heute zuletzt kontrolliert",
|
||||
"nl": "Dit object is vandaag voor het laatst gecontroleerd"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
"point",
|
||||
"centroid"
|
||||
],
|
||||
"icon": "circle:white;./assets/layers/waste_disposal/waste_disposal.svg"
|
||||
"icon": "circle:white;./assets/layers/waste_disposal/waste_disposal.svg",
|
||||
"iconSize": "20,20"
|
||||
}
|
||||
],
|
||||
"presets": [
|
||||
|
|
|
@ -86,6 +86,7 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"payment-options-split",
|
||||
{
|
||||
"id": "coin",
|
||||
"question": {
|
||||
|
@ -205,7 +206,8 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"level"
|
||||
"level",
|
||||
"check_date"
|
||||
],
|
||||
"mapRendering": [
|
||||
{
|
||||
|
@ -235,8 +237,15 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"allowMove": {
|
||||
"enableImproveAccuracy": true,
|
||||
"enableRelocation": true
|
||||
},
|
||||
"deletion": true,
|
||||
"filter": [
|
||||
"open_now"
|
||||
"open_now",
|
||||
"accepts_debit_cards",
|
||||
"accepts_credit_cards"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
|
|
@ -63,10 +63,10 @@
|
|||
{
|
||||
"builtin": "food",
|
||||
"override": {
|
||||
"minzoom": 19,
|
||||
"minzoom": 18,
|
||||
"filter": null,
|
||||
"name": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -262,21 +262,8 @@
|
|||
"searching": "Cercant…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Afegir-lo a la pantalla d'inici</h3>Pots afegir aquesta web a la pantalla d'inici del teu smartphone per a que es vegi més nadiu. Apreta al botó 'Afegir a l'inici' a la barra d'adreces URL per fer-ho.",
|
||||
"copiedToClipboard": "Enllaç copiat al portapapers",
|
||||
"downloadCustomTheme": "Descarrega la configuració per a aquest tema",
|
||||
"downloadCustomThemeHelp": "Un contribuïdor amb experiència pot utilitzar un arxiu per a millorar el vostre tema",
|
||||
"editThemeDescription": "Afegir o canviar preguntes d'aquesta petició",
|
||||
"editThisTheme": "Editar aquest repte",
|
||||
"embedIntro": "<h3>Inclou-ho a la teva pàgina web</h3>Per favor, inclou aquest mapa dins de la teva pàgina web. <br/>T'animem a que ho facis, no cal que demanis permís. <br/> És gratuït, i sempre ho serà. A més gent que ho faci servir més valuós serà.",
|
||||
"fsAddNew": "Activar el botó d'afegir nou PDI'",
|
||||
"fsGeolocation": "Activar el botó de 'geolocalitza'm' (només mòbil)",
|
||||
"fsIncludeCurrentBackgroundMap": "Incloure l'opció de fons actual <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Incloure les opcions de capa actual",
|
||||
"fsIncludeCurrentLocation": "Incloure localització actual",
|
||||
"fsLayerControlToggle": "Iniciar el control de capes avançat",
|
||||
"fsLayers": "Activar el control de capes",
|
||||
"fsSearch": "Activar la barra de cerca",
|
||||
"fsUserbadge": "Activar el botó d'entrada",
|
||||
"fsWelcomeMessage": "Mostra el missatge emergent de benvinguda i pestanyes associades",
|
||||
"intro": "<h3>Comparteix aquest mapa</h3> Comparteix aquest mapa copiant l'enllaç de sota i enviant-lo a amics i família:",
|
||||
|
|
|
@ -262,21 +262,8 @@
|
|||
"searching": "Hledání…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Přidejte stránku na domovskou obrazovku</h3>Tuto webovou stránku můžete snadno přidat na domovskou obrazovku vašeho smartphonu, aby působila nativně. Klikněte na tlačítko „Přidat na domovskou obrazovku“ na panelu s adresou URL.",
|
||||
"copiedToClipboard": "Odkaz zkopírovaný do schránky",
|
||||
"downloadCustomTheme": "Stáhnout konfiguraci tohoto tématu",
|
||||
"downloadCustomThemeHelp": "Zkušený přispěvatel může tento soubor použít k vylepšení vašeho tématu",
|
||||
"editThemeDescription": "Přidejte nebo změňte otázky k tomuto tématu mapy",
|
||||
"editThisTheme": "Upravit toto téma",
|
||||
"embedIntro": "<h3>Vložte mapu na své webové stránky</h3>Prosíme, vložte tuto mapu na své webové stránky. <br/>Doporučujeme vám to udělat - nemusíte ani žádat o povolení. <br/> Je a vždy to bude zdarma. Čím více lidí bude projekt používat, tím bude cennější.",
|
||||
"fsAddNew": "Povolit tlačítko „přidat nový bod zájmu“",
|
||||
"fsGeolocation": "Povolit tlačítko „geolokovat mě“ (pouze pro mobilní zařízení)",
|
||||
"fsIncludeCurrentBackgroundMap": "Zahrnout aktuální volbu pozadí <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Zahrnout aktuální volby vrstvy",
|
||||
"fsIncludeCurrentLocation": "Zahrnout aktuální polohu",
|
||||
"fsLayerControlToggle": "Začněte s rozšířeným ovládáním vrstvy",
|
||||
"fsLayers": "Povolit ovládání vrstev",
|
||||
"fsSearch": "Povolit vyhledávací pole",
|
||||
"fsUserbadge": "Povolit tlačítko přihlášení",
|
||||
"fsWelcomeMessage": "Zobrazit vyskakovací okno s uvítací zprávou a související karty",
|
||||
"intro": "<h3>Sdílejte tuto mapu</h3> Sdílejte tuto mapu zkopírováním níže uvedeného odkazu a jeho zasláním přátelům a rodině:",
|
||||
|
|
|
@ -203,21 +203,8 @@
|
|||
"searching": "Søger…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Tilføj til din hjemmeskærm</h3>Du kan let tilføje denne hjemmeside til din smartphones hjemmeskærm for at få den integreret. Klik på 'Tilføj til hjemmeskærm' knappen i URL-feltet for at gøre det.",
|
||||
"copiedToClipboard": "Link kopierer til udklipsholder",
|
||||
"downloadCustomTheme": "Download opsætningen for dette tema",
|
||||
"downloadCustomThemeHelp": "En erfaren bidragyder kan bruge denne fil til at forbedre dit tema",
|
||||
"editThemeDescription": "Tilføj eller ret spørgsmål til dette korttema",
|
||||
"editThisTheme": "Ret dette tema",
|
||||
"embedIntro": "<h3>Indlejr på din hjemmeside</h3>Indlejr venligst dette kort på din hjemmeside. <br>Vi tilskynder dig til det - du behøver ikke engang at spørge om tilladelse. <br> Det det frit og gratis og vil altid være det. Jo flere der bruger det, jo mere værdifuldt bliver det.",
|
||||
"fsAddNew": "Slå 'tilføj nyt POI' knappen til",
|
||||
"fsGeolocation": "Slå 'geolocate-me' knappen til (kun mobil)",
|
||||
"fsIncludeCurrentBackgroundMap": "Inkluder det aktuelle baggrundsvalg <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Inkluder det aktuelle valg af lag",
|
||||
"fsIncludeCurrentLocation": "Inkludere det aktuelle sted",
|
||||
"fsLayerControlToggle": "Start med lagkontrollen udvidet",
|
||||
"fsLayers": "Slå lagkontrollen til",
|
||||
"fsSearch": "Slå søgefeltet til",
|
||||
"fsUserbadge": "Slå loginknappen til",
|
||||
"fsWelcomeMessage": "Vis velkomstbeskeden og tilknyttede faner",
|
||||
"intro": "<h3>Del dette kort</h3>Del dette kort ved at kopiere linket nedenunder og send det til venner og familie:",
|
||||
|
|
|
@ -304,21 +304,8 @@
|
|||
"searching": "Suchen …"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Karte zum Startbildschirm hinzufügen</h3> Fügen Sie diese Webseite zum Startbildschirm Ihres Smartphones hinzu, um ein natives Gefühl zu erhalten. Klicken Sie dazu in der Adressleiste auf die Schaltfläche 'Zum Startbildschirm hinzufügen'.",
|
||||
"copiedToClipboard": "Verknüpfung in Zwischenablage kopiert",
|
||||
"downloadCustomTheme": "Konfiguration für diese Karte herunterladen",
|
||||
"downloadCustomThemeHelp": "Ein erfahrener Mitwirkender kann diese Datei verwenden, um Ihr Thema zu verbessern",
|
||||
"editThemeDescription": "Fragen zu diesem Thema hinzufügen oder ändern",
|
||||
"editThisTheme": "Dieses Thema bearbeiten",
|
||||
"embedIntro": "<h3>Karte in Webseiten einbetten</h3>Betten Sie diese Karte in Ihre Webseite ein. <br/>Wir ermutigen Sie gern dazu - Sie müssen nicht mal um Erlaubnis fragen.<br/> Die Karte ist kostenlos und wird es immer sein. Je mehr Leute die Karte benutzen, desto wertvoller wird sie.",
|
||||
"fsAddNew": "Schaltfläche 'neuen POI hinzufügen' aktivieren",
|
||||
"fsGeolocation": "Schaltfläche 'Mich geolokalisieren' aktivieren (nur mobil)",
|
||||
"fsIncludeCurrentBackgroundMap": "Aktuellen Hintergrund übernehmen <b>({name})</b>",
|
||||
"fsIncludeCurrentLayers": "Aktuelle Ebenenauswahl übernehmen",
|
||||
"fsIncludeCurrentLocation": "Aktuelle Position übernehmen",
|
||||
"fsLayerControlToggle": "Ausgeklappte Ebenenauswahl anzeigen",
|
||||
"fsLayers": "Ebenensteuerung aktivieren",
|
||||
"fsSearch": "Suchleiste aktivieren",
|
||||
"fsUserbadge": "Anmeldefeld aktivieren",
|
||||
"fsWelcomeMessage": "Begrüßung und Registerkarten anzeigen",
|
||||
"intro": "<h3>Karte teilen</h3> Mit dem folgenden Link können Sie diese Karte mit Freunden und Familie teilen:",
|
||||
|
|
|
@ -304,21 +304,9 @@
|
|||
"searching": "Searching…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Add to your home screen</h3>You can easily add this website to your smartphone home screen for a native feel. Click the 'Add to home screen' button in the URL bar to do this.",
|
||||
"copiedToClipboard": "Link copied to clipboard",
|
||||
"downloadCustomTheme": "Download the configuration for this theme",
|
||||
"downloadCustomThemeHelp": "An experienced contributor can use this file to improve your theme",
|
||||
"editThemeDescription": "Add or change questions to this map theme",
|
||||
"editThisTheme": "Edit this theme",
|
||||
"documentation": "For more information on available URL-parameters, <a href='https://github.com/pietervdvn/MapComplete/blob/develop/Docs/URL_Parameters.md' target='_blank'>consult the documentation</a>",
|
||||
"embedIntro": "<h3>Embed on your website</h3>Please, embed this map into your website. <br/>We encourage you to do it - you don't even have to ask permission. <br/> It is free, and always will be. The more people are using this, the more valuable it becomes.",
|
||||
"fsAddNew": "Enable the 'add new POI' button",
|
||||
"fsGeolocation": "Enable the 'geolocate-me' button (mobile only)",
|
||||
"fsIncludeCurrentBackgroundMap": "Include the current background choice <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Include the current layer choices",
|
||||
"fsIncludeCurrentLocation": "Include current location",
|
||||
"fsLayerControlToggle": "Start with the layer control expanded",
|
||||
"fsLayers": "Enable the layer control",
|
||||
"fsSearch": "Enable the search bar",
|
||||
"fsUserbadge": "Enable the login button",
|
||||
"fsWelcomeMessage": "Show the welcome message popup and associated tabs",
|
||||
"intro": "<h3>Share this map</h3> Share this map by copying the link below and sending it to friends and family:",
|
||||
|
@ -610,6 +598,11 @@
|
|||
"feedback": "This is not a valid email address",
|
||||
"noAt": "An e-mail address must contain an @"
|
||||
},
|
||||
"fediverse": {
|
||||
"description": "A fediverse handle, often @username@server.tld",
|
||||
"feedback": "A fediverse handle consists of @username@server.tld or is a link to a profile",
|
||||
"invalidHost": "{host} is not a valid hostname"
|
||||
},
|
||||
"float": {
|
||||
"description": "a number",
|
||||
"feedback": "This is not a number"
|
||||
|
|
|
@ -61,8 +61,6 @@
|
|||
"searching": "Serĉante…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"editThisTheme": "Modifi ĉi tiun etoson",
|
||||
"fsSearch": "Ŝalti la serĉbreton",
|
||||
"fsUserbadge": "Ŝalti la salutbutonon"
|
||||
},
|
||||
"skip": "Preterpasi ĉi tiun demandon",
|
||||
|
|
|
@ -209,21 +209,8 @@
|
|||
"searching": "Buscando…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Añadir a la pantalla de inicio</h3>Puedes añadir esta web en la pantalla de inicio de tu smartphone para que se vea más nativo. Aprieta el botón 'añadir a inicio' en la barra de direcciones URL para hacerlo.",
|
||||
"copiedToClipboard": "Enlace copiado en el portapapeles",
|
||||
"downloadCustomTheme": "Descargar la configuración para este tema",
|
||||
"downloadCustomThemeHelp": "Un contributor con experiencia puede utilizar este archivo para mejorar tu tema",
|
||||
"editThemeDescription": "Añadir o cambiar preguntas de este reto",
|
||||
"editThisTheme": "Editar este reto",
|
||||
"embedIntro": "<h3>Inclúyelo en tu página web</h3>Incluye este mapa en tu página web. <br/> Te animamos a que lo hagas, no hace falta que pidas permiso. <br/> Es gratis, y siempre lo será. A más gente que lo use más valioso será.",
|
||||
"fsAddNew": "Activar el botón de añadir nuevo PDI'",
|
||||
"fsGeolocation": "Activar el botón de 'geolocalízame' (només mòbil)",
|
||||
"fsIncludeCurrentBackgroundMap": "Incluir la opción de fondo actual <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Incluir las opciones de capa actual",
|
||||
"fsIncludeCurrentLocation": "Incluir localización actual",
|
||||
"fsLayerControlToggle": "Iniciar el control de capas avanzado",
|
||||
"fsLayers": "Activar el control de capas",
|
||||
"fsSearch": "Activar la barra de búsqueda",
|
||||
"fsUserbadge": "Activar el botón de entrada",
|
||||
"fsWelcomeMessage": "Muestra el mensaje emergente de bienvenida y pestañas asociadas",
|
||||
"intro": "<h3>Comparte este mapa</h3> Comparte este mapa copiando el enlace de debajo y enviándolo a amigos y familia:",
|
||||
|
|
|
@ -242,21 +242,8 @@
|
|||
"searching": "Chargement…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Ajouter à votre page d'accueil</h3> Vous pouvez facilement ajouter la carte à votre écran d'accueil de téléphone. Cliquer sur le bouton 'ajouter à l'écran d'accueil' dans la barre d'adresse pour effectuer cette tâche.",
|
||||
"copiedToClipboard": "Lien copié dans le presse-papier",
|
||||
"downloadCustomTheme": "Téléchargez la configuration de ce thème",
|
||||
"downloadCustomThemeHelp": "Un contributeur expérimenté peut utiliser ce fichier pour améliorer votre thème",
|
||||
"editThemeDescription": "Ajouter ou modifier des questions à ce thème",
|
||||
"editThisTheme": "Editer ce thème",
|
||||
"embedIntro": "<h3>Incorporer à votre site Web</h3>Ajouter la carte à votre site Web. <br>Nous vous y encourageons – pas besoin de permission.<br> C'est gratuit et pour toujours. Plus de personnes l'utilisent, mieux c'est.",
|
||||
"fsAddNew": "Activer le bouton 'ajouter un POI'",
|
||||
"fsGeolocation": "Activer le bouton 'Localisez-moi' (seulement sur mobile)",
|
||||
"fsIncludeCurrentBackgroundMap": "Include le choix actuel d'arrière plan <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Inclure la couche selectionnée",
|
||||
"fsIncludeCurrentLocation": "Inclure l'emplacement actuel",
|
||||
"fsLayerControlToggle": "Démarrer avec le contrôle des couches ouvert",
|
||||
"fsLayers": "Activer le contrôle des couches",
|
||||
"fsSearch": "Activer la barre de recherche",
|
||||
"fsUserbadge": "Activer le bouton de connexion",
|
||||
"fsWelcomeMessage": "Afficher le message de bienvenue et les onglets associés",
|
||||
"intro": "<h3>Partager cette carte</h3> Partagez cette carte en copiant le lien suivant et envoyez-le à vos amis :",
|
||||
|
|
|
@ -101,19 +101,8 @@
|
|||
"searching": "Procurando..."
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Engadir á pantalla de inicio</h3>Podes engadir esta web na pantalla de inicio do teu smartphone para que se vexa máis nativo. Preme o botón 'engadir ó inicio' na barra de enderezos URL para facelo.",
|
||||
"copiedToClipboard": "Ligazón copiada ó portapapeis",
|
||||
"editThemeDescription": "Engadir ou mudar preguntas a este tema do mapa",
|
||||
"editThisTheme": "Editar este tema",
|
||||
"embedIntro": "<h3>Inclúeo na túa páxina web</h3>Inclúe este mapa na túa páxina web. <br/> Animámoche a que o fagas, non fai falla que pidas permiso. <br/> É de balde, e sempre será. Canta máis xente que o empregue máis valioso será.",
|
||||
"fsAddNew": "Activar o botón de 'engadir novo PDI'",
|
||||
"fsGeolocation": "Activar o botón de 'xeolocalizarme' (só móbil)",
|
||||
"fsIncludeCurrentBackgroundMap": "Incluír a opción de fondo actual <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Incluír as opcións de capa actual",
|
||||
"fsIncludeCurrentLocation": "Incluír localización actual",
|
||||
"fsLayerControlToggle": "Comenza co control de capas expandido",
|
||||
"fsLayers": "Activar o control de capas",
|
||||
"fsSearch": "Activar a barra de procura",
|
||||
"fsUserbadge": "Activar botón de inicio de sesión",
|
||||
"fsWelcomeMessage": "Amosar a xanela emerxente da mensaxe de benvida e as lapelas asociadas",
|
||||
"intro": "<h3>Comparte este mapa</h3> Comparte este mapa copiando a ligazón de embaixo e enviándoa ás amizades e familia:",
|
||||
|
|
|
@ -196,19 +196,8 @@
|
|||
"searching": "Keresés…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Hozzáadás a kezdőképernyőhöz</h3> Könnyedén hozzáadhatod ezt a weboldalt az okostelefon kezdőképernyőjéhez a natív hangulat érdekében. Ehhez kattints az URL-sávban a „Hozzáadás a kezdőképernyőhöz” gombra.",
|
||||
"copiedToClipboard": "Link a vágólapra másolva",
|
||||
"editThemeDescription": "Térképtémához tartozó kérdések hozzáadása vagy módosítása",
|
||||
"editThisTheme": "Téma szerkesztése",
|
||||
"embedIntro": "<h3>Beágyazás egy weboldalon</h3>Kérjük, illeszd be ezt a térképet a weboldalba. <br>Biztatunk, tedd meg – még engedélyt sem kell kérned. <br> Ingyenes, és az is marad. Minél többen használják, annál értékesebbé válik. A pipákra kattintva módosíthatod a paramétereket:",
|
||||
"fsAddNew": "„Új érdekes pont (POI) hozzáadása” gomb engedélyezése",
|
||||
"fsGeolocation": "„Saját helyem megjelenítése” gomb engedélyezése (csak mobileszközön)",
|
||||
"fsIncludeCurrentBackgroundMap": "Tartalmazza a jelenleg kiválasztott hátteret (<b>{name}</b>)",
|
||||
"fsIncludeCurrentLayers": "Tartalmazza a jelenleg kiválasztott rétegeket",
|
||||
"fsIncludeCurrentLocation": "Tartalmazza az aktuális helyet",
|
||||
"fsLayerControlToggle": "Kezdés kibontott rétegvezérlővel",
|
||||
"fsLayers": "Rétegvezérlő engedélyezése",
|
||||
"fsSearch": "Keresősáv engedélyezése",
|
||||
"fsUserbadge": "Bejelentkezési gomb engedélyezése",
|
||||
"fsWelcomeMessage": "Felugró üdvözlő üzenet és kapcsolódó fülek megjelenítése",
|
||||
"intro": "<h3>Térkép megosztása</h3> Oszd meg ezt a térképet! Másold ki az alábbi linket, és küldd el a barátaidnak és a családodnak:",
|
||||
|
|
|
@ -106,15 +106,6 @@
|
|||
},
|
||||
"sharescreen": {
|
||||
"copiedToClipboard": "Tautan disalin ke papan klip",
|
||||
"editThemeDescription": "Tambahkan atau ubah pertanyaan ke tema peta ini",
|
||||
"editThisTheme": "Sunting tema ini",
|
||||
"fsAddNew": "Aktifkan tombol 'tambah POI baru'",
|
||||
"fsGeolocation": "Aktifkan tombol 'geolocate-me' (hanya seluler)",
|
||||
"fsIncludeCurrentBackgroundMap": "Sertakan pilihan latar belakang saat ini <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Sertakan pilihan lapisan saat ini",
|
||||
"fsIncludeCurrentLocation": "Sertakan lokasi saat ini",
|
||||
"fsLayers": "Aktifkan kontrol lapisan",
|
||||
"fsSearch": "Aktifkan bilah pencarian",
|
||||
"fsUserbadge": "Aktifkan tombol masuk",
|
||||
"fsWelcomeMessage": "Tampilkan popup pesan selamat datang dan tab terkait",
|
||||
"thanksForSharing": "Terima kasih telah berbagi!"
|
||||
|
|
|
@ -204,21 +204,8 @@
|
|||
"searching": "Ricerca…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Aggiungi alla tua schermata Home</h3>Puoi aggiungere facilmente questo sito web alla schermata Home del tuo smartphone. Per farlo, clicca sul pulsante ‘Aggiungi a schermata Home’ nella barra degli indirizzi.",
|
||||
"copiedToClipboard": "Collegamento copiato negli appunti",
|
||||
"downloadCustomTheme": "Scarica la configurazione di questo argomento",
|
||||
"downloadCustomThemeHelp": "Una persona esperta può utilizzare questo file per migliorare il tuo argomento",
|
||||
"editThemeDescription": "Aggiungi o modifica le domande a questo tema della mappa",
|
||||
"editThisTheme": "Modifica questo tema",
|
||||
"embedIntro": "<h3>Incorpora nel tuo sito web</h3>Siamo lieti se vorrai includere questa cartina nel tuo sito web.<br>Ti incoraggiamo a farlo (non devi neanche chieder il permesso).<br>È gratuito e lo sarà per sempre. Più persone lo useranno e più valore acquisirà.",
|
||||
"fsAddNew": "Abilita il pulsante ‘aggiungi nuovo PDI’",
|
||||
"fsGeolocation": "Abilita il pusante ‘geo-localizzami’ (solo da mobile)",
|
||||
"fsIncludeCurrentBackgroundMap": "Includi lo sfondo attualmente selezionato <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Includi i livelli correntemente selezionati",
|
||||
"fsIncludeCurrentLocation": "Includi la posizione attuale",
|
||||
"fsLayerControlToggle": "Inizia con il pannello dei livelli aperto",
|
||||
"fsLayers": "Abilita il controllo dei livelli",
|
||||
"fsSearch": "Abilita la barra di ricerca",
|
||||
"fsUserbadge": "Abilita il pulsante di accesso",
|
||||
"fsWelcomeMessage": "Mostra il messaggio di benvenuto e le schede associate",
|
||||
"intro": "<h3>Condividi questa mappa</h3>Condividi questa mappa copiando il collegamento qua sotto e inviandolo ad amici o parenti:",
|
||||
|
|
|
@ -101,19 +101,8 @@
|
|||
"searching": "検索中…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>ホーム画面に追加する</h3>このサイトはスマートフォンのホーム画面に簡単に追加でき、ネイティブな雰囲気になります。これを行うには、URLバーの「ホーム画面に追加ボタン」をクリックします。",
|
||||
"copiedToClipboard": "クリップボードにコピーされたリンク",
|
||||
"editThemeDescription": "このマップテーマに質問を追加または変更する",
|
||||
"editThisTheme": "このテーマを編集",
|
||||
"embedIntro": "<h3>お客様のWebサイトに埋め込む</h3> この地図をお客様のWebサイトに埋め込みます。<br>許可を得る必要もありませんので、ぜひご利用ください。<br>無料であり、常に利用できます。使う人が増えれば増えるほど、価値が増大します。",
|
||||
"fsAddNew": "[新しいPOIの追加]ボタンを有効にする",
|
||||
"fsGeolocation": "[geolocate-me]ボタンを有効にする(モバイルのみ)",
|
||||
"fsIncludeCurrentBackgroundMap": "現在の背景の選択肢<b>{name}</b>を含める",
|
||||
"fsIncludeCurrentLayers": "現在のレイヤの選択肢を含める",
|
||||
"fsIncludeCurrentLocation": "現在の場所を含める",
|
||||
"fsLayerControlToggle": "レイヤコントロールを展開して開始",
|
||||
"fsLayers": "レイヤコントロールを有効にする",
|
||||
"fsSearch": "検索バーを有効にする",
|
||||
"fsUserbadge": "ログインボタンを有効にする",
|
||||
"fsWelcomeMessage": "ウェルカムメッセージのポップアップと関連するタブを表示します",
|
||||
"intro": "<h3>このマップを共有</h3>このマップを共有するには、次のリンクをコピーして、友人や家族に送信します。",
|
||||
|
|
|
@ -2065,6 +2065,13 @@
|
|||
"question": "Har en halalmenu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Har en halalmenu"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Restauranter og fastfood"
|
||||
|
|
|
@ -4754,6 +4754,13 @@
|
|||
"question": "Halal Gerichte im Angebot"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Halal Gerichte im Angebot"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Restaurants und Imbisse",
|
||||
|
@ -5855,9 +5862,6 @@
|
|||
"mappings": {
|
||||
"0": {
|
||||
"then": "Dies ist eine Wohnstraße, auf der eine Höchstgeschwindigkeit von 20 km/h gilt"
|
||||
},
|
||||
"1": {
|
||||
"then": "Dies ist eine Wohnstraße, auf der eine Höchstgeschwindigkeit von 20 km/h gilt"
|
||||
}
|
||||
},
|
||||
"question": "Wie hoch ist die zulässige Höchstgeschwindigkeit, die man auf dieser Straße fahren darf?",
|
||||
|
|
|
@ -4739,6 +4739,12 @@
|
|||
"options": {
|
||||
"0": {
|
||||
"question": "Has a vegetarian menu"
|
||||
},
|
||||
"1": {
|
||||
"question": "Only fastfood buisinesses"
|
||||
},
|
||||
"2": {
|
||||
"question": "Only restaurants"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -4755,6 +4761,13 @@
|
|||
"question": "Has a halal menu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Has a halal menu"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Restaurants and fast food",
|
||||
|
@ -5856,9 +5869,6 @@
|
|||
"mappings": {
|
||||
"0": {
|
||||
"then": "This is a living street, which has a maxspeed of 20km/h"
|
||||
},
|
||||
"1": {
|
||||
"then": "This is a living street, which has a maxspeed of 20km/h"
|
||||
}
|
||||
},
|
||||
"question": "What is the legal maximum speed one is allowed to drive on this road?",
|
||||
|
|
|
@ -2610,6 +2610,13 @@
|
|||
"question": "Tiene menú halah"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Tiene menú halah"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Restaurantes y comida rápida",
|
||||
|
|
|
@ -3278,6 +3278,13 @@
|
|||
"question": "A un menu halal"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "A un menu halal"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Restaurants et nourriture rapide",
|
||||
|
|
|
@ -4530,6 +4530,13 @@
|
|||
"question": "Heeft een halal menu"
|
||||
}
|
||||
}
|
||||
},
|
||||
"5": {
|
||||
"options": {
|
||||
"0": {
|
||||
"question": "Heeft een halal menu"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Eetgelegenheden",
|
||||
|
@ -5553,9 +5560,6 @@
|
|||
"mappings": {
|
||||
"0": {
|
||||
"then": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h"
|
||||
},
|
||||
"1": {
|
||||
"then": "Dit is een woonerf en heeft dus een maximale snelheid van 20km/h"
|
||||
}
|
||||
},
|
||||
"question": "Wat is de legale maximumsnelheid voor deze weg?",
|
||||
|
|
|
@ -233,20 +233,8 @@
|
|||
"searching": "Søker …"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Legg til på hjemmeskjermen din</h3>Du kan enkelt legge til denne nettsiden på din smarttelefon-hjemmeskjerm for å få det hele integrert. Klikk på «Legg til på hjemmeskjerm»-knappen i nettadressefeltet for å gjøre dette.",
|
||||
"copiedToClipboard": "Lenke kopiert til utklippstavle",
|
||||
"downloadCustomTheme": "Last ned oppsettet for dette temaet",
|
||||
"downloadCustomThemeHelp": "En dreven bidragsyter kan bruke denne filen for å forbedre temaet ditt",
|
||||
"editThemeDescription": "Legg til eller endre spørsmål for dette karttemaet",
|
||||
"editThisTheme": "Rediger dette temaet",
|
||||
"embedIntro": "<h3>Bygg inn på nettsiden din</h3>Legg til dette kartet på nettsiden din. <br/>Du oppfordres til å gjøre dette, og trenger ikke å spørre om tillatelse. <br/> Det er fritt, og vil alltid være det. Desto flere som bruker dette, desto mer verdifullt blir det.",
|
||||
"fsGeolocation": "Skru på «Geolokaliser meg»-knappen (kun for mobil)",
|
||||
"fsIncludeCurrentBackgroundMap": "Inkluder nåværende bakgrunnsvalg <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Inkluder nåværende lagvalg",
|
||||
"fsIncludeCurrentLocation": "Inkluder nåværende posisjon",
|
||||
"fsLayerControlToggle": "Start med lagkontrollen utvidet",
|
||||
"fsLayers": "Skru på lagkontrollen",
|
||||
"fsSearch": "Skru på søkefeltet",
|
||||
"fsUserbadge": "Skru på innloggingsknappen",
|
||||
"fsWelcomeMessage": "Vis velkomst-oppsprettsmeldinger og tilknyttede faner",
|
||||
"intro": "<h3>Del dette kartet</h3> Del dette kartet ved å kopiere lenken nedenfor og sende den til venner og familie:",
|
||||
|
|
|
@ -304,21 +304,8 @@
|
|||
"searching": "Aan het zoeken…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Voeg toe aan je thuisscherm</h3>Je kan eenvoudigweg deze website aan het thuisscherm van je smartphone toevoegen voor een \"native feel\"",
|
||||
"copiedToClipboard": "Link gekopieerd naar klembord",
|
||||
"downloadCustomTheme": "Download de instellingen van dit thema",
|
||||
"downloadCustomThemeHelp": "Een ervaren bijdrager kan op basis van dit bestand je thema verder verbeteren",
|
||||
"editThemeDescription": "Pas vragen aan of voeg vragen toe aan dit kaartthema",
|
||||
"editThisTheme": "Pas dit thema aan",
|
||||
"embedIntro": "<h3>Plaats dit op je website</h3>Voeg dit kaartje toe op je eigen website.<br/>We moedigen dit zelfs aan - je hoeft geen toestemming te vragen.<br/> Het is gratis en zal dat altijd blijven. Hoe meer het gebruikt wordt, hoe waardevoller",
|
||||
"fsAddNew": "Activeer het toevoegen van nieuwe POI",
|
||||
"fsGeolocation": "Toon het knopje voor geolocalisatie (enkel op mobiel)",
|
||||
"fsIncludeCurrentBackgroundMap": "Gebruik de huidige achtergrond <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Toon enkel de huidig getoonde lagen",
|
||||
"fsIncludeCurrentLocation": "Start op de huidige locatie",
|
||||
"fsLayerControlToggle": "Toon de laagbediening meteen volledig",
|
||||
"fsLayers": "Toon de knop voor laagbediening",
|
||||
"fsSearch": "Activeer de zoekbalk",
|
||||
"fsUserbadge": "Activeer de login-knop",
|
||||
"fsWelcomeMessage": "Toon het welkomstbericht en de bijhorende tabbladen",
|
||||
"intro": "<h3>Deel deze kaart</h3> Kopieer onderstaande link om deze kaart naar vrienden en familie door te sturen:",
|
||||
|
|
|
@ -55,7 +55,6 @@
|
|||
"searching": "کھوجیا جا رہا اے۔ ۔ ۔"
|
||||
},
|
||||
"sharescreen": {
|
||||
"editThisTheme": "ایہہ تھیم سودھو",
|
||||
"thanksForSharing": "ٹھیک اے، مہربانی۔"
|
||||
},
|
||||
"weekdays": {
|
||||
|
|
|
@ -98,19 +98,8 @@
|
|||
"searching": "Szukanie…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3> Dodaj do ekranu głównego</h3>Możesz łatwo dodać tę stronę do ekranu głównego smartfona, aby poczuć się jak w domu. Kliknij przycisk \"Dodaj do ekranu głównego\" na pasku adresu URL, aby to zrobić.",
|
||||
"copiedToClipboard": "Link został skopiowany do schowka",
|
||||
"editThemeDescription": "Dodaj lub zmień pytania do tego motywu mapy",
|
||||
"editThisTheme": "Edytuj ten motyw",
|
||||
"embedIntro": "<h3>Umieść na swojej stronie internetowej</h3>Proszę, umieść tę mapę na swojej stronie internetowej. <br>Zachęcamy cię do tego - nie musisz nawet pytać o zgodę. <br>Jest ona darmowa i zawsze będzie. Im więcej osób jej używa, tym bardziej staje się wartościowa.",
|
||||
"fsAddNew": "Włącz przycisk \"Dodaj nowe POI\"",
|
||||
"fsGeolocation": "Włącz przycisk „Zlokalizuj mnie” (tylko na urządzeniach mobilnych)",
|
||||
"fsIncludeCurrentBackgroundMap": "Dołącz bieżący wybór tła <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Uwzględnij wybór bieżącej warstwy",
|
||||
"fsIncludeCurrentLocation": "Uwzględnij bieżącą lokalizację",
|
||||
"fsLayerControlToggle": "Zacznij od rozwiniętej kontroli warstw",
|
||||
"fsLayers": "Włącz kontrolę warstw",
|
||||
"fsSearch": "Włącz pasek wyszukiwania",
|
||||
"fsUserbadge": "Włącz przycisk logowania",
|
||||
"fsWelcomeMessage": "Pokaż wyskakujące okienko wiadomości powitalnej i powiązane zakładki",
|
||||
"intro": "<h3> Udostępnij tę mapę</h3> Udostępnij tę mapę, kopiując poniższy link i wysyłając ją do przyjaciół i rodziny:",
|
||||
|
|
|
@ -242,21 +242,8 @@
|
|||
"searching": "A procurar…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Adicionar ao seu ecrã inicial</h3> Pode adicionar facilmente este site ao ecrã inicial do seu telemóvel. Para isso clique no botão 'Adicionar ao ecrã inicial' na barra de URL.",
|
||||
"copiedToClipboard": "Hiperligação copiada para a área de transferência",
|
||||
"downloadCustomTheme": "Descarregar a configuração para este tema",
|
||||
"downloadCustomThemeHelp": "Um colaborador experiente pode utilizar este ficheiro para melhorar o seu tema",
|
||||
"editThemeDescription": "Adicionar ou alterar perguntas deste tema",
|
||||
"editThisTheme": "Editar este tema",
|
||||
"embedIntro": "<h3>Incorporar no seu site</h3>Por favor, insira este mapa no seu site. <br>Encorajamos a fazê-lo - nem precisa de pedir permissão. <br> É grátis e sempre será. Quanto mais pessoas estiverem a usar isto, mais valioso se torna.",
|
||||
"fsAddNew": "Ativar o botão 'adicionar novo POI'",
|
||||
"fsGeolocation": "Ativar o botão 'localizar-me geograficamente' (apenas telemóvel)",
|
||||
"fsIncludeCurrentBackgroundMap": "Incluir a escolha de fundo atual <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Incluir as opções de camada atuais",
|
||||
"fsIncludeCurrentLocation": "Incluir localização atual",
|
||||
"fsLayerControlToggle": "Começar com o controlo de camadas expandido",
|
||||
"fsLayers": "Ativar o controlo das camadas",
|
||||
"fsSearch": "Ativar a barra de pesquisa",
|
||||
"fsUserbadge": "Ativar o botão de iniciar sessão",
|
||||
"fsWelcomeMessage": "Mostrar a janela com a mensagem de boas-vindas e separadores associados",
|
||||
"intro": "<h3>Partilhar este mapa</h3> Partilhe este mapa copiando a hiperligação abaixo e enviando-a a amigos e familiares:",
|
||||
|
|
|
@ -98,19 +98,8 @@
|
|||
"searching": "Procurando…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Adicionar à sua tela inicial</h3>Você pode adicionar facilmente este site à tela inicial do smartphone para uma sensação nativa. Clique no botão 'adicionar à tela inicial' na barra de URL para fazer isso.",
|
||||
"copiedToClipboard": "Link copiado para a área de transferência",
|
||||
"editThemeDescription": "Adicione ou altere perguntas a este tema do mapa",
|
||||
"editThisTheme": "Editar neste tema",
|
||||
"embedIntro": "<h3>Incorpore em seu site</h3>Por favor, incorpore este mapa em seu site.<br>Nós o encorajamos a fazer isso - você nem precisa pedir permissão.<br>É gratuito e sempre será. Quanto mais pessoas usarem isso, mais valioso se tornará.",
|
||||
"fsAddNew": "Habilite o botão 'adicionar novo POI'",
|
||||
"fsGeolocation": "Ative o botão 'localizar-me geograficamente' (apenas para celular)",
|
||||
"fsIncludeCurrentBackgroundMap": "Incluir a escolha de fundo atual <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Incluir as opções de camada atuais",
|
||||
"fsIncludeCurrentLocation": "Incluir localização atual",
|
||||
"fsLayerControlToggle": "Iniciar com o controle de camada expandido",
|
||||
"fsLayers": "Ativar o controle de camada",
|
||||
"fsSearch": "Ativar a barra de pesquisa",
|
||||
"fsUserbadge": "Habilite o botão de login",
|
||||
"fsWelcomeMessage": "Mostra o pop-up da mensagem de boas-vindas e as guias associadas",
|
||||
"intro": "<h3>Compartilhe este mapa</h3> Compartilhe este mapa copiando o link abaixo e enviando-o para amigos e familiares:",
|
||||
|
|
|
@ -114,19 +114,8 @@
|
|||
"searching": "Поиск…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>Добавить на домашний экран</h3>Вы можете легко добавить этот сайт на домашний экран вашего смартфона. Для этого нажмите кнопку \"Добавить на главный экран\" в строке URL.",
|
||||
"copiedToClipboard": "Ссылка скопирована в буфер обмена",
|
||||
"editThemeDescription": "Добавить или изменить вопросы к этой теме карты",
|
||||
"editThisTheme": "Редактировать эту тему",
|
||||
"embedIntro": "<h3>Встроить на свой сайт</h3>Пожалуйста, вставьте эту карту на свой сайт.<br>Мы призываем вас сделать это - вам даже не нужно спрашивать разрешения.<br>Карта бесплатна и всегда будет бесплатной. Чем больше людей пользуются ею, тем более ценной она становится.",
|
||||
"fsAddNew": "Включить кнопку \"добавить новую точку интереса\"",
|
||||
"fsGeolocation": "Включить кнопку \"найди меня\" (только в мобильной версии)",
|
||||
"fsIncludeCurrentBackgroundMap": "Включить текущий фоновый слой <b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "Включить текущие выбранные слои",
|
||||
"fsIncludeCurrentLocation": "Включить текущее местоположение карты",
|
||||
"fsLayerControlToggle": "Открыть панель выбора слоя",
|
||||
"fsLayers": "Включить выбор слоя карты",
|
||||
"fsSearch": "Включить строку поиска",
|
||||
"fsUserbadge": "Включить кнопку входа в систему",
|
||||
"fsWelcomeMessage": "Показать всплывающее окно с приветствием и соответствующие вкладки",
|
||||
"intro": "<h3>Поделиться этой картой</h3> Поделитесь этой картой, скопировав ссылку ниже и отправив её друзьям и близким:",
|
||||
|
|
|
@ -226,21 +226,8 @@
|
|||
"searching": "搜尋中…"
|
||||
},
|
||||
"sharescreen": {
|
||||
"addToHomeScreen": "<h3>新增到你主頁畫面</h3>你可以輕易將這網站加到你智慧型手機的主頁畫面,在網址列點選 '新增到主頁按鈕'來做這件事情。",
|
||||
"copiedToClipboard": "複製連結到簡貼簿",
|
||||
"downloadCustomTheme": "下載這個主題的設定",
|
||||
"downloadCustomThemeHelp": "有經驗的貢獻者能使用這個檔案來改善你的主題",
|
||||
"editThemeDescription": "新增或改變這個地圖主題的問題",
|
||||
"editThisTheme": "編輯這個主題",
|
||||
"embedIntro": "<h3>嵌入到你的網站</h3>請考慮將這份地圖嵌入您的網站。<br>地圖毋須額外授權,非常歡迎您多加利用。<br>一切都是免費的,而且之後也是免費的,越有更多人使用,則越顯得它的價值。",
|
||||
"fsAddNew": "啟用'新增新的興趣點'按鈕",
|
||||
"fsGeolocation": "啟用'地理定位自身'按鈕 (只有行動版本)",
|
||||
"fsIncludeCurrentBackgroundMap": "包含目前背景選擇<b>{name}</b>",
|
||||
"fsIncludeCurrentLayers": "包含目前選擇圖層",
|
||||
"fsIncludeCurrentLocation": "包含目前位置",
|
||||
"fsLayerControlToggle": "開始時擴展圖層控制",
|
||||
"fsLayers": "啟用圖層控制",
|
||||
"fsSearch": "啟用搜尋列",
|
||||
"fsUserbadge": "啟用登入按鈕",
|
||||
"fsWelcomeMessage": "顯示歡迎訊息以及相關頁籤",
|
||||
"intro": "<h3>分享這地圖</h3>複製下面的連結來向朋友與家人分享這份地圖:",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "mapcomplete",
|
||||
"version": "0.31.2",
|
||||
"version": "0.31.3",
|
||||
"repository": "https://github.com/pietervdvn/MapComplete",
|
||||
"description": "A small website to edit OSM easily",
|
||||
"bugs": "https://github.com/pietervdvn/MapComplete/issues",
|
||||
|
|
|
@ -388,11 +388,11 @@ WriteFile("./Docs/Tags_format.md", TagUtils.generateDocs(), ["src/Logic/Tags/Tag
|
|||
}
|
||||
}
|
||||
for (const usedBuiltin of usedBuiltins) {
|
||||
var using = layersUsingBuiltin.get(usedBuiltin)
|
||||
if (using === undefined) {
|
||||
const usingLayers = layersUsingBuiltin.get(usedBuiltin)
|
||||
if (usingLayers === undefined) {
|
||||
layersUsingBuiltin.set(usedBuiltin, [layer.id])
|
||||
} else {
|
||||
using.push(layer.id)
|
||||
usingLayers.push(layer.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -413,7 +413,7 @@ WriteFile("./Docs/URL_Parameters.md", QueryParameterDocumentation.GenerateQueryP
|
|||
"src/Logic/Web/QueryParameters.ts",
|
||||
"src/UI/QueryParameterDocumentation.ts",
|
||||
])
|
||||
if (fakedom === undefined || window === undefined) {
|
||||
if (fakedom === undefined) {
|
||||
throw "FakeDom not initialized"
|
||||
}
|
||||
QueryParameters.GetQueryParameter(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import ScriptUtils from "./ScriptUtils"
|
||||
import { Utils } from "../Utils"
|
||||
import { Utils } from "../src/Utils"
|
||||
import * as fs from "fs"
|
||||
|
||||
async function main(args: string[]) {
|
||||
|
|
|
@ -72,6 +72,11 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
return
|
||||
}
|
||||
|
||||
if (neededTiles.total > 100) {
|
||||
console.error("Too much tiles to download!")
|
||||
return
|
||||
}
|
||||
|
||||
this.isRunning.setData(true)
|
||||
try {
|
||||
const tileNumbers = Tiles.MapRange(neededTiles, (x, y) => {
|
||||
|
@ -133,7 +138,6 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
}
|
||||
|
||||
private async LoadTile(z: number, x: number, y: number): Promise<void> {
|
||||
console.log("OsmFeatureSource: loading ", z, x, y, "from", this._backend)
|
||||
if (z >= 22) {
|
||||
throw "This is an absurd high zoom level"
|
||||
}
|
||||
|
@ -145,6 +149,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
if (this._downloadedTiles.has(index)) {
|
||||
return
|
||||
}
|
||||
console.log("OsmFeatureSource: loading ", z, x, y, "from", this._backend)
|
||||
this._downloadedTiles.add(index)
|
||||
|
||||
const bbox = BBox.fromTile(z, x, y)
|
||||
|
|
|
@ -51,10 +51,9 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
*/
|
||||
public readonly layoutToUse: LayoutConfig
|
||||
|
||||
public readonly featureSwitchUserbadge: UIEventSource<boolean>
|
||||
public readonly featureSwitchEnableLogin: UIEventSource<boolean>
|
||||
public readonly featureSwitchSearch: UIEventSource<boolean>
|
||||
public readonly featureSwitchBackgroundSelection: UIEventSource<boolean>
|
||||
public readonly featureSwitchAddNew: UIEventSource<boolean>
|
||||
public readonly featureSwitchWelcomeMessage: UIEventSource<boolean>
|
||||
public readonly featureSwitchCommunityIndex: UIEventSource<boolean>
|
||||
public readonly featureSwitchExtraLinkEnabled: UIEventSource<boolean>
|
||||
|
@ -78,10 +77,10 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
|
||||
// Helper function to initialize feature switches
|
||||
|
||||
this.featureSwitchUserbadge = FeatureSwitchUtils.initSwitch(
|
||||
"fs-userbadge",
|
||||
this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch(
|
||||
"fs-enable-login",
|
||||
layoutToUse?.enableUserBadge ?? true,
|
||||
"Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode."
|
||||
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode."
|
||||
)
|
||||
this.featureSwitchSearch = FeatureSwitchUtils.initSwitch(
|
||||
"fs-search",
|
||||
|
@ -99,11 +98,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
layoutToUse?.enableLayers ?? true,
|
||||
"Disables/Enables the filter view"
|
||||
)
|
||||
this.featureSwitchAddNew = FeatureSwitchUtils.initSwitch(
|
||||
"fs-add-new",
|
||||
layoutToUse?.enableAddNewPoints ?? true,
|
||||
"Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)"
|
||||
)
|
||||
|
||||
this.featureSwitchWelcomeMessage = FeatureSwitchUtils.initSwitch(
|
||||
"fs-welcome-message",
|
||||
true,
|
||||
|
@ -201,12 +196,6 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
|
|||
)
|
||||
)
|
||||
|
||||
this.featureSwitchUserbadge.addCallbackAndRun((userbadge) => {
|
||||
if (!userbadge) {
|
||||
this.featureSwitchAddNew.setData(false)
|
||||
}
|
||||
})
|
||||
|
||||
this.backgroundLayerId = QueryParameters.GetQueryParameter(
|
||||
"background",
|
||||
layoutToUse?.defaultBackgroundId ?? "osm",
|
||||
|
|
|
@ -4,14 +4,13 @@
|
|||
import { UIEventSource } from "../UIEventSource"
|
||||
import Hash from "./Hash"
|
||||
import { Utils } from "../../Utils"
|
||||
import doc = Mocha.reporters.doc
|
||||
|
||||
export class QueryParameters {
|
||||
static defaults: Record<string, string> = {}
|
||||
static documentation: Map<string, string> = new Map<string, string>()
|
||||
private static order: string[] = ["layout", "test", "z", "lat", "lon"]
|
||||
protected static readonly _wasInitialized: Set<string> = new Set()
|
||||
protected static readonly knownSources: Record<string, UIEventSource<string>> = {}
|
||||
private static order: string[] = ["layout", "test", "z", "lat", "lon"]
|
||||
private static initialized = false
|
||||
|
||||
public static GetQueryParameter(
|
||||
|
@ -74,6 +73,7 @@ export class QueryParameters {
|
|||
this.init()
|
||||
return QueryParameters._wasInitialized.has(key)
|
||||
}
|
||||
|
||||
public static initializedParameters(): ReadonlyArray<string> {
|
||||
return Array.from(QueryParameters._wasInitialized.keys())
|
||||
}
|
||||
|
@ -108,14 +108,12 @@ export class QueryParameters {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query parameters of the page location
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
private static Serialize() {
|
||||
const parts = []
|
||||
public static GetParts(exclude?: Set<string>) {
|
||||
const parts: string[] = []
|
||||
for (const key of QueryParameters.order) {
|
||||
if (exclude?.has(key)) {
|
||||
continue
|
||||
}
|
||||
if (QueryParameters.knownSources[key]?.data === undefined) {
|
||||
continue
|
||||
}
|
||||
|
@ -134,6 +132,16 @@ export class QueryParameters {
|
|||
encodeURIComponent(QueryParameters.knownSources[key].data)
|
||||
)
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query parameters of the page location
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
private static Serialize() {
|
||||
const parts = QueryParameters.GetParts()
|
||||
if (!Utils.runningFromConsole) {
|
||||
// Don't pollute the history every time a parameter changes
|
||||
try {
|
||||
|
@ -151,4 +159,8 @@ export class QueryParameters {
|
|||
QueryParameters._wasInitialized.clear()
|
||||
QueryParameters.order = []
|
||||
}
|
||||
|
||||
static GetDefaultFor(key: string): string {
|
||||
return QueryParameters.defaults[key]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,25 @@
|
|||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import Hash from "./Hash"
|
||||
import { MenuState } from "../../Models/MenuState"
|
||||
|
||||
export default class ThemeViewStateHashActor {
|
||||
private readonly _state: ThemeViewState
|
||||
|
||||
public static readonly documentation = [
|
||||
"The URL-hash can contain multiple values:",
|
||||
"",
|
||||
"- The id of the currently selected object, e.g. `node/1234`",
|
||||
"- The currently opened menu view",
|
||||
"- The base64-encoded JSON-file specifying a custom theme (only when loading)",
|
||||
"",
|
||||
"### Possible hashes to open a menu",
|
||||
"",
|
||||
"The possible hashes are:",
|
||||
"",
|
||||
MenuState._menuviewTabs.map((tab) => "`menu:" + tab + "`").join(","),
|
||||
MenuState._themeviewTabs.map((tab) => "`theme-menu:" + tab + "`").join(","),
|
||||
]
|
||||
|
||||
/**
|
||||
* Converts the hash to the appropriate themeview state and, vice versa, sets the hash.
|
||||
*
|
||||
|
@ -100,7 +116,7 @@ export default class ThemeViewStateHashActor {
|
|||
|
||||
private loadStateFromHash(hash: string) {
|
||||
const state = this._state
|
||||
const parts = hash.split(";")
|
||||
const parts = hash.split(":")
|
||||
outer: for (const { toggle, name, showOverOthers, submenu } of state.guistate.allToggles) {
|
||||
for (const part of parts) {
|
||||
if (part === name) {
|
||||
|
|
|
@ -50,11 +50,15 @@ export class MenuState {
|
|||
)
|
||||
public highlightedUserSetting: UIEventSource<string> = new UIEventSource<string>(undefined)
|
||||
|
||||
constructor(themeid: string = "") {
|
||||
constructor(shouldOpenWelcomeMessage: boolean, themeid: string = "") {
|
||||
// Note: this class is _not_ responsible to update the Hash, @see ThemeViewStateHashActor for this
|
||||
if (themeid) {
|
||||
themeid += "-"
|
||||
}
|
||||
this.themeIsOpened = LocalStorageSource.GetParsed(themeid + "thememenuisopened", true)
|
||||
this.themeIsOpened = LocalStorageSource.GetParsed(
|
||||
themeid + "thememenuisopened",
|
||||
shouldOpenWelcomeMessage
|
||||
)
|
||||
this.themeViewTabIndex = LocalStorageSource.GetParsed(themeid + "themeviewtabindex", 0)
|
||||
this.themeViewTab = this.themeViewTabIndex.sync(
|
||||
(i) => MenuState._themeviewTabs[i],
|
||||
|
|
|
@ -23,6 +23,27 @@ export interface TagRenderingConfigJson {
|
|||
| Translatable
|
||||
| { special: Record<string, string | Record<string, string>> & { type: string } }
|
||||
|
||||
/**
|
||||
* question: what icon should be shown next to the 'render' value?
|
||||
* An icon shown next to the rendering; typically shown pretty small
|
||||
* This is only shown next to the "render" value
|
||||
* Type: icon
|
||||
*/
|
||||
icon?:
|
||||
| string
|
||||
| {
|
||||
/**
|
||||
* The path to the icon
|
||||
* Type: icon
|
||||
*/
|
||||
path: string
|
||||
/**
|
||||
* A hint to mapcomplete on how to render this icon within the mapping.
|
||||
* This is translated to 'mapping-icon-<classtype>', so defining your own in combination with a custom CSS is possible (but discouraged)
|
||||
*/
|
||||
class?: "small" | "medium" | "large" | string
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* question: When should this item be shown?
|
||||
|
|
|
@ -19,6 +19,8 @@ import { Paragraph } from "../../UI/Base/Paragraph"
|
|||
import Svg from "../../Svg"
|
||||
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
|
||||
|
||||
export interface Icon {}
|
||||
|
||||
export interface Mapping {
|
||||
readonly if: UploadableTag
|
||||
readonly ifnot?: UploadableTag
|
||||
|
@ -45,6 +47,8 @@ export interface Mapping {
|
|||
export default class TagRenderingConfig {
|
||||
public readonly id: string
|
||||
public readonly render?: TypedTranslation<object>
|
||||
public readonly renderIcon?: string
|
||||
public readonly renderIconClass?: string
|
||||
public readonly question?: TypedTranslation<object>
|
||||
public readonly questionhint?: TypedTranslation<object>
|
||||
public readonly condition?: TagsFilter
|
||||
|
@ -58,7 +62,7 @@ export default class TagRenderingConfig {
|
|||
|
||||
public readonly freeform?: {
|
||||
readonly key: string
|
||||
readonly type: string
|
||||
readonly type: ValidatorType
|
||||
readonly placeholder: Translation
|
||||
readonly addExtraTags: UploadableTag[]
|
||||
readonly inline: boolean
|
||||
|
@ -124,6 +128,13 @@ export default class TagRenderingConfig {
|
|||
this.questionhint = Translations.T(json.questionHint, translationKey + ".questionHint")
|
||||
this.description = Translations.T(json.description, translationKey + ".description")
|
||||
this.condition = TagUtils.Tag(json.condition ?? { and: [] }, `${context}.condition`)
|
||||
if (typeof json.icon === "string") {
|
||||
this.renderIcon = json.icon
|
||||
this.renderIconClass = "small"
|
||||
} else if (typeof json.icon === "object") {
|
||||
this.renderIcon = json.icon.path
|
||||
this.renderIconClass = json.icon.class
|
||||
}
|
||||
this.metacondition = TagUtils.Tag(
|
||||
json.metacondition ?? { and: [] },
|
||||
`${context}.metacondition`
|
||||
|
@ -135,7 +146,17 @@ export default class TagRenderingConfig {
|
|||
) {
|
||||
throw `Freeform.addExtraTags should be a list of strings - not a single string (at ${context})`
|
||||
}
|
||||
const type = json.freeform.type ?? "string"
|
||||
if (
|
||||
json.freeform.type &&
|
||||
Validators.availableTypes.indexOf(<any>json.freeform.type) < 0
|
||||
) {
|
||||
throw `At ${context}: invalid type, perhaps you meant ${Utils.sortedByLevenshteinDistance(
|
||||
json.freeform.key,
|
||||
<any>Validators.availableTypes,
|
||||
(s) => <any>s
|
||||
)}`
|
||||
}
|
||||
const type: ValidatorType = <any>json.freeform.type ?? "string"
|
||||
|
||||
let placeholder: Translation = Translations.T(json.freeform.placeholder)
|
||||
if (placeholder === undefined) {
|
||||
|
@ -230,19 +251,21 @@ export default class TagRenderingConfig {
|
|||
if (txt.indexOf("{" + this.freeform.key + ":") >= 0) {
|
||||
continue
|
||||
}
|
||||
if (txt.indexOf("{canonical(" + this.freeform.key + ")") >= 0) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (txt.indexOf("{translated(" + this.freeform.key + ")") >= 0) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
this.freeform.type === "opening_hours" &&
|
||||
txt.indexOf("{opening_hours_table(") >= 0
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const keyFirstArg = ["canonical", "fediverse_link", "translated"]
|
||||
if (
|
||||
keyFirstArg.some(
|
||||
(funcName) => txt.indexOf(`{${funcName}(${this.freeform.key}`) >= 0
|
||||
)
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
this.freeform.type === "wikidata" &&
|
||||
txt.indexOf("{wikipedia(" + this.freeform.key) >= 0
|
||||
|
@ -528,7 +551,7 @@ export default class TagRenderingConfig {
|
|||
*/
|
||||
public GetRenderValueWithImage(
|
||||
tags: Record<string, string>
|
||||
): { then: TypedTranslation<any>; icon?: string } | undefined {
|
||||
): { then: TypedTranslation<any>; icon?: string; iconClass?: string } | undefined {
|
||||
if (this.condition !== undefined) {
|
||||
if (!this.condition.matchesProperties(tags)) {
|
||||
return undefined
|
||||
|
@ -547,7 +570,7 @@ export default class TagRenderingConfig {
|
|||
}
|
||||
|
||||
if (this.freeform?.key === undefined || tags[this.freeform.key] !== undefined) {
|
||||
return { then: this.render }
|
||||
return { then: this.render, icon: this.renderIcon, iconClass: this.renderIconClass }
|
||||
}
|
||||
|
||||
return undefined
|
||||
|
@ -628,7 +651,7 @@ export default class TagRenderingConfig {
|
|||
*
|
||||
* @param singleSelectedMapping (Only used if multiAnswer == false): the single mapping to apply. Use (mappings.length) for the freeform
|
||||
* @param multiSelectedMapping (Only used if multiAnswer == true): all the mappings that must be applied. Set multiSelectedMapping[mappings.length] to use the freeform as well
|
||||
* @param currentProperties: The current properties of the object for which the question should be answered
|
||||
* @param currentProperties The current properties of the object for which the question should be answered
|
||||
*/
|
||||
public constructChangeSpecification(
|
||||
freeformValue: string | undefined,
|
||||
|
@ -691,38 +714,42 @@ export default class TagRenderingConfig {
|
|||
return undefined
|
||||
}
|
||||
return and
|
||||
} else {
|
||||
// Is at least one mapping shown in the answer?
|
||||
const someMappingIsShown = this.mappings.some((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
return !m.hideInAnswer
|
||||
}
|
||||
const isHidden = m.hideInAnswer.matchesProperties(currentProperties)
|
||||
return !isHidden
|
||||
})
|
||||
// If all mappings are hidden for the current tags, we can safely assume that we should use the freeform key
|
||||
const useFreeform =
|
||||
freeformValue !== undefined &&
|
||||
(singleSelectedMapping === this.mappings.length || !someMappingIsShown)
|
||||
if (useFreeform) {
|
||||
return new And([
|
||||
new Tag(this.freeform.key, freeformValue),
|
||||
...(this.freeform.addExtraTags ?? []),
|
||||
])
|
||||
} else if (singleSelectedMapping !== undefined) {
|
||||
return new And([
|
||||
this.mappings[singleSelectedMapping].if,
|
||||
...(this.mappings[singleSelectedMapping].addExtraTags ?? []),
|
||||
])
|
||||
} else {
|
||||
console.warn("TagRenderingConfig.ConstructSpecification has a weird fallback for", {
|
||||
freeformValue,
|
||||
singleSelectedMapping,
|
||||
multiSelectedMapping,
|
||||
currentProperties,
|
||||
})
|
||||
return undefined
|
||||
}
|
||||
|
||||
// Is at least one mapping shown in the answer?
|
||||
const someMappingIsShown = this.mappings.some((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
return !m.hideInAnswer
|
||||
}
|
||||
const isHidden = m.hideInAnswer.matchesProperties(currentProperties)
|
||||
return !isHidden
|
||||
})
|
||||
// If all mappings are hidden for the current tags, we can safely assume that we should use the freeform key
|
||||
const useFreeform =
|
||||
freeformValue !== undefined &&
|
||||
(singleSelectedMapping === this.mappings.length ||
|
||||
!someMappingIsShown ||
|
||||
singleSelectedMapping === undefined)
|
||||
if (useFreeform) {
|
||||
return new And([
|
||||
new Tag(this.freeform.key, freeformValue),
|
||||
...(this.freeform.addExtraTags ?? []),
|
||||
])
|
||||
} else if (singleSelectedMapping !== undefined) {
|
||||
return new And([
|
||||
this.mappings[singleSelectedMapping].if,
|
||||
...(this.mappings[singleSelectedMapping].addExtraTags ?? []),
|
||||
])
|
||||
} else {
|
||||
console.error("TagRenderingConfig.ConstructSpecification has a weird fallback for", {
|
||||
freeformValue,
|
||||
singleSelectedMapping,
|
||||
multiSelectedMapping,
|
||||
currentProperties,
|
||||
useFreeform,
|
||||
})
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,15 +110,18 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
|
||||
constructor(layout: LayoutConfig) {
|
||||
this.layout = layout
|
||||
this.guistate = new MenuState(layout.id)
|
||||
this.featureSwitches = new FeatureSwitchState(layout)
|
||||
this.guistate = new MenuState(
|
||||
this.featureSwitches.featureSwitchWelcomeMessage.data,
|
||||
layout.id
|
||||
)
|
||||
this.map = new UIEventSource<MlMap>(undefined)
|
||||
const initial = new InitialMapPositioning(layout)
|
||||
this.mapProperties = new MapLibreAdaptor(this.map, initial)
|
||||
const geolocationState = new GeoLocationState()
|
||||
|
||||
this.featureSwitches = new FeatureSwitchState(layout)
|
||||
this.featureSwitchIsTesting = this.featureSwitches.featureSwitchIsTesting
|
||||
this.featureSwitchUserbadge = this.featureSwitches.featureSwitchUserbadge
|
||||
this.featureSwitchUserbadge = this.featureSwitches.featureSwitchEnableLogin
|
||||
|
||||
this.osmConnection = new OsmConnection({
|
||||
dryRun: this.featureSwitches.featureSwitchIsTesting,
|
||||
|
@ -465,7 +468,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
|
|||
|
||||
new ShowDataLayer(this.map, {
|
||||
features: new FilteringFeatureSource(last_click_layer, last_click),
|
||||
doShowLayer: new ImmutableStore(true),
|
||||
doShowLayer: this.featureSwitches.featureSwitchEnableLogin,
|
||||
layer: last_click_layer.layerDef,
|
||||
selectedElement: this.selectedElement,
|
||||
selectedLayer: this.selectedLayer,
|
||||
|
|
121
src/UI/BigComponents/ShareScreen.svelte
Normal file
121
src/UI/BigComponents/ShareScreen.svelte
Normal file
|
@ -0,0 +1,121 @@
|
|||
<script lang="ts">/**
|
||||
* A screen showing:
|
||||
* - A link to share the current view
|
||||
* - Some query parameters that can be enabled/disabled
|
||||
* - The code to embed MC as IFrame
|
||||
*/
|
||||
|
||||
import ThemeViewState from "../../Models/ThemeViewState";
|
||||
import { QueryParameters } from "../../Logic/Web/QueryParameters";
|
||||
import Tr from "../Base/Tr.svelte";
|
||||
import Translations from "../i18n/Translations";
|
||||
import { Utils } from "../../Utils";
|
||||
import Svg from "../../Svg";
|
||||
import ToSvelte from "../Base/ToSvelte.svelte";
|
||||
import { DocumentDuplicateIcon } from "@rgossiaux/svelte-heroicons/outline";
|
||||
|
||||
export let state: ThemeViewState;
|
||||
const tr = Translations.t.general.sharescreen;
|
||||
|
||||
let url = window.location;
|
||||
let linkToShare: string = undefined;
|
||||
/**
|
||||
* In some cases (local deploys, custom themes), we need to set the URL to `/theme.html?layout=xyz` instead of `/xyz?...`
|
||||
*/
|
||||
let needsThemeRedirect = url.port !== "" || url.hostname.match(/^[0-9]/) || !state.layout.official;
|
||||
let layoutId = state.layout.id;
|
||||
let baseLink = url.protocol + "//" + url.host + "/" + (needsThemeRedirect ? "theme.html?layout=" + layoutId + "&" : layoutId + "?");
|
||||
|
||||
let showWelcomeMessage = true;
|
||||
let enableLogin = true;
|
||||
$: {
|
||||
const layout = state.layout;
|
||||
let excluded = Utils.NoNull([
|
||||
showWelcomeMessage ? undefined : "fs-welcome-message",
|
||||
enableLogin ? undefined : "fs-enable-login"
|
||||
]);
|
||||
linkToShare = baseLink + QueryParameters.GetParts(new Set(excluded))
|
||||
.concat(excluded.map(k => k + "=" + false))
|
||||
.join("&");
|
||||
if (layout.definitionRaw !== undefined) {
|
||||
linkToShare += "&userlayout=" + (layout.definedAtUrl ?? layout.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function shareCurrentLink() {
|
||||
await navigator.share({
|
||||
title: Translations.W(state.layout.title)?.ConstructElement().textContent ?? "MapComplete",
|
||||
text: Translations.W(state.layout.description)?.ConstructElement().textContent ?? "",
|
||||
url: linkToShare
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
let isCopied = false;
|
||||
|
||||
async function copyCurrentLink() {
|
||||
await navigator.clipboard.writeText(linkToShare);
|
||||
isCopied = true;
|
||||
await Utils.waitFor(5000);
|
||||
isCopied = false;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
<Tr t={tr.intro} />
|
||||
<div class="flex">
|
||||
{#if typeof navigator?.share === "function"}
|
||||
<button class="w-8 h-8 p-1 shrink-0" on:click={shareCurrentLink}>
|
||||
<ToSvelte construct={Svg.share_svg()} />
|
||||
</button>
|
||||
{/if}
|
||||
{#if navigator.clipboard !== undefined}
|
||||
<button class="w-8 h-8 p-1 shrink-0 no-image-background" on:click={copyCurrentLink}>
|
||||
<DocumentDuplicateIcon />
|
||||
</button>
|
||||
{/if}
|
||||
<div class="literal-code" on:click={e => Utils.selectTextIn(e.target)}>
|
||||
{linkToShare}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-center">
|
||||
|
||||
{#if isCopied}
|
||||
<Tr t={tr.copiedToClipboard} cls="thanks m-2" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<Tr t={ tr.embedIntro} />
|
||||
|
||||
|
||||
<div class="flex flex-col my-1 link-underline">
|
||||
|
||||
<label>
|
||||
<input bind:checked={showWelcomeMessage} type="checkbox" />
|
||||
<Tr t={tr.fsWelcomeMessage} />
|
||||
</label>
|
||||
|
||||
|
||||
<label>
|
||||
<input bind:checked={enableLogin} type="checkbox" />
|
||||
<Tr t={tr.fsUserbadge} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="literal-code m-1">
|
||||
<span class="literal-code iframe-code-block"> <br />
|
||||
<iframe src="${url}" <br />
|
||||
allow="geolocation" width="100%" height="100%" style="min-width: 250px; min-height: 250px" <br />
|
||||
title="${state.layout.title?.txt ?? "MapComplete" } with MapComplete"> <br />
|
||||
</iframe> <br />
|
||||
</span>
|
||||
</div>
|
||||
<Tr t={tr.documentation} cls="link-underline"/>
|
||||
</div>
|
|
@ -1,256 +0,0 @@
|
|||
import { VariableUiElement } from "../Base/VariableUIElement"
|
||||
import { Translation } from "../i18n/Translation"
|
||||
import Svg from "../../Svg"
|
||||
import Combine from "../Base/Combine"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { Utils } from "../../Utils"
|
||||
import Translations from "../i18n/Translations"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import { InputElement } from "../Input/InputElement"
|
||||
import { CheckBox } from "../Input/Checkboxes"
|
||||
import { SubtleButton } from "../Base/SubtleButton"
|
||||
import LZString from "lz-string"
|
||||
import { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
|
||||
export class ShareScreen extends Combine {
|
||||
constructor(state: SpecialVisualizationState) {
|
||||
const layout = state?.layout
|
||||
const tr = Translations.t.general.sharescreen
|
||||
|
||||
const optionCheckboxes: InputElement<boolean>[] = []
|
||||
const optionParts: Store<string>[] = []
|
||||
|
||||
const includeLocation = new CheckBox(tr.fsIncludeCurrentLocation, true)
|
||||
optionCheckboxes.push(includeLocation)
|
||||
|
||||
const currentLocation = state.mapProperties.location
|
||||
const zoom = state.mapProperties.zoom
|
||||
|
||||
optionParts.push(
|
||||
includeLocation.GetValue().map(
|
||||
(includeL) => {
|
||||
if (currentLocation === undefined) {
|
||||
return null
|
||||
}
|
||||
if (includeL) {
|
||||
return [
|
||||
["z", zoom.data],
|
||||
["lat", currentLocation.data?.lat],
|
||||
["lon", currentLocation.data?.lon],
|
||||
]
|
||||
.filter((p) => p[1] !== undefined)
|
||||
.map((p) => p[0] + "=" + p[1])
|
||||
.join("&")
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
[currentLocation, zoom]
|
||||
)
|
||||
)
|
||||
|
||||
function fLayerToParam(flayer: {
|
||||
isDisplayed: UIEventSource<boolean>
|
||||
layerDef: LayerConfig
|
||||
}) {
|
||||
if (flayer.isDisplayed.data) {
|
||||
return null // Being displayed is the default
|
||||
}
|
||||
return "layer-" + flayer.layerDef.id + "=" + flayer.isDisplayed.data
|
||||
}
|
||||
|
||||
const currentLayer: Store<
|
||||
{ id: string; name: string | Record<string, string> } | undefined
|
||||
> = state.mapProperties.rasterLayer.map((l) => l?.properties)
|
||||
const currentBackground = new VariableUiElement(
|
||||
currentLayer.map((layer) => {
|
||||
return tr.fsIncludeCurrentBackgroundMap.Subs({ name: layer?.name ?? "" })
|
||||
})
|
||||
)
|
||||
const includeCurrentBackground = new CheckBox(currentBackground, true)
|
||||
optionCheckboxes.push(includeCurrentBackground)
|
||||
optionParts.push(
|
||||
includeCurrentBackground.GetValue().map(
|
||||
(includeBG) => {
|
||||
if (includeBG) {
|
||||
return "background=" + currentLayer.data?.id
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
[currentLayer]
|
||||
)
|
||||
)
|
||||
|
||||
const includeLayerChoices = new CheckBox(tr.fsIncludeCurrentLayers, true)
|
||||
optionCheckboxes.push(includeLayerChoices)
|
||||
|
||||
optionParts.push(
|
||||
includeLayerChoices.GetValue().map(
|
||||
(includeLayerSelection) => {
|
||||
if (includeLayerSelection) {
|
||||
return Utils.NoNull(
|
||||
Array.from(state.layerState.filteredLayers.values()).map(fLayerToParam)
|
||||
).join("&")
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
},
|
||||
Array.from(state.layerState.filteredLayers.values()).map(
|
||||
(flayer) => flayer.isDisplayed
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
const switches = [
|
||||
{ urlName: "fs-userbadge", human: tr.fsUserbadge },
|
||||
{ urlName: "fs-search", human: tr.fsSearch },
|
||||
{ urlName: "fs-welcome-message", human: tr.fsWelcomeMessage },
|
||||
{ urlName: "fs-layers", human: tr.fsLayers },
|
||||
{ urlName: "fs-add-new", human: tr.fsAddNew },
|
||||
{ urlName: "fs-geolocation", human: tr.fsGeolocation },
|
||||
]
|
||||
|
||||
for (const swtch of switches) {
|
||||
const checkbox = new CheckBox(Translations.W(swtch.human))
|
||||
optionCheckboxes.push(checkbox)
|
||||
optionParts.push(
|
||||
checkbox.GetValue().map((isEn) => {
|
||||
if (isEn) {
|
||||
return null
|
||||
} else {
|
||||
return `${swtch.urlName}=false`
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (layout.definitionRaw !== undefined) {
|
||||
optionParts.push(new UIEventSource("userlayout=" + (layout.definedAtUrl ?? layout.id)))
|
||||
}
|
||||
|
||||
const options = new Combine(optionCheckboxes).SetClass("flex flex-col")
|
||||
const url = (currentLocation ?? new UIEventSource(undefined)).map(() => {
|
||||
const host = window.location.host
|
||||
let path = window.location.pathname
|
||||
path = path.substr(0, path.lastIndexOf("/"))
|
||||
let id = layout.id.toLowerCase()
|
||||
if (layout.definitionRaw !== undefined) {
|
||||
id = "theme.html"
|
||||
}
|
||||
let literalText = `https://${host}${path}/${id}`
|
||||
|
||||
let hash = ""
|
||||
if (layout.definedAtUrl === undefined && layout.definitionRaw !== undefined) {
|
||||
hash = "#" + LZString.compressToBase64(Utils.MinifyJSON(layout.definitionRaw))
|
||||
}
|
||||
const parts = Utils.NoEmpty(
|
||||
Utils.NoNull(optionParts.map((eventSource) => eventSource.data))
|
||||
)
|
||||
if (parts.length === 0) {
|
||||
return literalText + hash
|
||||
}
|
||||
return literalText + "?" + parts.join("&") + hash
|
||||
}, optionParts)
|
||||
|
||||
const iframeCode = new VariableUiElement(
|
||||
url.map((url) => {
|
||||
return `<span class='literal-code iframe-code-block'>
|
||||
<iframe src="${url}" allow="geolocation" width="100%" height="100%" style="min-width: 250px; min-height: 250px" title="${
|
||||
layout.title?.txt ?? "MapComplete"
|
||||
} with MapComplete"></iframe>
|
||||
</span>`
|
||||
})
|
||||
)
|
||||
|
||||
const linkStatus = new UIEventSource<string | Translation>("")
|
||||
const link = new VariableUiElement(
|
||||
url.map(
|
||||
(url) =>
|
||||
`<input type="text" value=" ${url}" id="code-link--copyable" style="width:90%">`
|
||||
)
|
||||
).onClick(async () => {
|
||||
const shareData = {
|
||||
title: Translations.W(layout.title)?.ConstructElement().textContent ?? "",
|
||||
text: Translations.W(layout.description)?.ConstructElement().textContent ?? "",
|
||||
url: url.data,
|
||||
}
|
||||
|
||||
function rejected() {
|
||||
const copyText = document.getElementById("code-link--copyable")
|
||||
|
||||
// @ts-ignore
|
||||
copyText.select()
|
||||
// @ts-ignore
|
||||
copyText.setSelectionRange(0, 99999) /*For mobile devices*/
|
||||
|
||||
document.execCommand("copy")
|
||||
const copied = tr.copiedToClipboard.Clone()
|
||||
copied.SetClass("thanks")
|
||||
linkStatus.setData(copied)
|
||||
}
|
||||
|
||||
try {
|
||||
navigator
|
||||
.share(shareData)
|
||||
.then(() => {
|
||||
const thx = tr.thanksForSharing.Clone()
|
||||
thx.SetClass("thanks")
|
||||
linkStatus.setData(thx)
|
||||
}, rejected)
|
||||
.catch(rejected)
|
||||
} catch (err) {
|
||||
rejected()
|
||||
}
|
||||
})
|
||||
|
||||
let downloadThemeConfig: BaseUIElement = undefined
|
||||
if (layout.definitionRaw !== undefined) {
|
||||
const downloadThemeConfigAsJson = new SubtleButton(
|
||||
Svg.download_svg(),
|
||||
new Combine([tr.downloadCustomTheme, tr.downloadCustomThemeHelp.SetClass("subtle")])
|
||||
.onClick(() => {
|
||||
Utils.offerContentsAsDownloadableFile(
|
||||
layout.definitionRaw,
|
||||
layout.id + ".mapcomplete-theme-definition.json",
|
||||
{
|
||||
mimetype: "application/json",
|
||||
}
|
||||
)
|
||||
})
|
||||
.SetClass("flex flex-col")
|
||||
)
|
||||
let editThemeConfig: BaseUIElement = undefined
|
||||
if (layout.definedAtUrl === undefined) {
|
||||
const patchedDefinition = JSON.parse(layout.definitionRaw)
|
||||
patchedDefinition["language"] = Object.keys(patchedDefinition.title)
|
||||
editThemeConfig = new SubtleButton(
|
||||
Svg.pencil_svg(),
|
||||
"Edit this theme on the custom theme generator",
|
||||
{
|
||||
url: `https://pietervdvn.github.io/mc/legacy/070/customGenerator.html#${btoa(
|
||||
JSON.stringify(patchedDefinition)
|
||||
)}`,
|
||||
}
|
||||
)
|
||||
}
|
||||
downloadThemeConfig = new Combine([
|
||||
downloadThemeConfigAsJson,
|
||||
editThemeConfig,
|
||||
]).SetClass("flex flex-col")
|
||||
}
|
||||
|
||||
super([
|
||||
tr.intro,
|
||||
link,
|
||||
new VariableUiElement(linkStatus),
|
||||
downloadThemeConfig,
|
||||
tr.addToHomeScreen,
|
||||
tr.embedIntro,
|
||||
options,
|
||||
iframeCode,
|
||||
])
|
||||
this.SetClass("flex flex-col link-underline")
|
||||
}
|
||||
}
|
|
@ -45,9 +45,7 @@
|
|||
<Tr t={layout.description} />
|
||||
<Tr t={Translations.t.general.welcomeExplanation.general} />
|
||||
{#if layout.layers.some((l) => l.presets?.length > 0)}
|
||||
<If condition={state.featureSwitches.featureSwitchAddNew}>
|
||||
<Tr t={Translations.t.general.welcomeExplanation.addNew} />
|
||||
</If>
|
||||
{/if}
|
||||
|
||||
<Tr t={layout.descriptionTail} />
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
return
|
||||
}
|
||||
|
||||
if (unit && isNaN(Number(v))) {
|
||||
if (unit !== undefined && isNaN(Number(v))) {
|
||||
value.setData(undefined)
|
||||
return
|
||||
}
|
||||
|
@ -75,6 +75,7 @@
|
|||
feedback?.setData(undefined)
|
||||
value.setData(v + (selectedUnit.data ?? ""))
|
||||
}
|
||||
|
||||
|
||||
onDestroy(_value.addCallbackAndRun((_) => setValues()))
|
||||
onDestroy(value.addCallbackAndRunD(fromUpstream => {
|
||||
|
|
|
@ -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,13 @@ export abstract class Validator {
|
|||
/**
|
||||
* What HTML-inputmode to use
|
||||
*/
|
||||
public readonly inputmode?: string
|
||||
public readonly inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search'
|
||||
public readonly textArea: boolean
|
||||
|
||||
constructor(
|
||||
name: string,
|
||||
explanation: string | BaseUIElement,
|
||||
inputmode?: string,
|
||||
inputmode?: 'none' | 'text' | 'tel' | 'url' | 'email' | 'numeric' | 'decimal' | 'search',
|
||||
textArea?: false | boolean
|
||||
) {
|
||||
this.name = name
|
||||
|
|
|
@ -22,6 +22,7 @@ import SimpleTagValidator from "./Validators/SimpleTagValidator"
|
|||
import ImageUrlValidator from "./Validators/ImageUrlValidator"
|
||||
import TagKeyValidator from "./Validators/TagKeyValidator"
|
||||
import TranslationValidator from "./Validators/TranslationValidator"
|
||||
import FediverseValidator from "./Validators/FediverseValidator"
|
||||
|
||||
export type ValidatorType = (typeof Validators.availableTypes)[number]
|
||||
|
||||
|
@ -47,6 +48,7 @@ export default class Validators {
|
|||
"simple_tag",
|
||||
"key",
|
||||
"translation",
|
||||
"fediverse",
|
||||
] as const
|
||||
|
||||
public static readonly AllValidators: ReadonlyArray<Validator> = [
|
||||
|
@ -70,6 +72,7 @@ export default class Validators {
|
|||
new SimpleTagValidator(),
|
||||
new TagKeyValidator(),
|
||||
new TranslationValidator(),
|
||||
new FediverseValidator(),
|
||||
]
|
||||
|
||||
private static _byType = Validators._byTypeConstructor()
|
||||
|
|
63
src/UI/InputElement/Validators/FediverseValidator.ts
Normal file
63
src/UI/InputElement/Validators/FediverseValidator.ts
Normal file
|
@ -0,0 +1,63 @@
|
|||
import {Validator} from "../Validator"
|
||||
import {Translation} from "../../i18n/Translation";
|
||||
import Translations from "../../i18n/Translations";
|
||||
|
||||
export default class FediverseValidator extends Validator {
|
||||
|
||||
public static readonly usernameAtServer: RegExp = /^@?(\w+)@((\w|\.)+)$/
|
||||
|
||||
constructor() {
|
||||
super("fediverse", "Validates fediverse addresses and normalizes them into `@username@server`-format");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an `@username@host`
|
||||
* @param s
|
||||
*/
|
||||
reformat(s: string): string {
|
||||
if(!s.startsWith("@")){
|
||||
s = "@"+s
|
||||
}
|
||||
if (s.match(FediverseValidator.usernameAtServer)) {
|
||||
return s
|
||||
}
|
||||
try {
|
||||
const url = new URL(s)
|
||||
const path = url.pathname
|
||||
if (path.match(/^\/\w+$/)) {
|
||||
return `@${path.substring(1)}@${url.hostname}`;
|
||||
}
|
||||
} catch (e) {
|
||||
// Nothing to do here
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
getFeedback(s: string): Translation | undefined {
|
||||
const match = s.match(FediverseValidator.usernameAtServer)
|
||||
console.log("Match:", match)
|
||||
if (match) {
|
||||
const host = match[2]
|
||||
try {
|
||||
const url = new URL("https://" + host)
|
||||
return undefined
|
||||
} catch (e) {
|
||||
return Translations.t.validation.fediverse.invalidHost.Subs({host})
|
||||
}
|
||||
}
|
||||
try {
|
||||
const url = new URL(s)
|
||||
const path = url.pathname
|
||||
if (path.match(/^\/\w+$/)) {
|
||||
return undefined
|
||||
}
|
||||
} catch (e) {
|
||||
// Nothing to do here
|
||||
}
|
||||
return Translations.t.validation.fediverse.feedback
|
||||
}
|
||||
|
||||
isValid(s): boolean {
|
||||
return this.getFeedback(s) === undefined
|
||||
|
||||
}
|
||||
}
|
|
@ -1,11 +1,12 @@
|
|||
import { Translation } from "../../i18n/Translation"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import { Validator } from "../Validator"
|
||||
import { ValidatorType } from "../Validators";
|
||||
|
||||
export default class FloatValidator extends Validator {
|
||||
inputmode = "decimal"
|
||||
inputmode: "decimal" = "decimal"
|
||||
|
||||
constructor(name?: string, explanation?: string) {
|
||||
constructor(name?: ValidatorType, explanation?: string) {
|
||||
super(name ?? "float", explanation ?? "A decimal number", "decimal")
|
||||
}
|
||||
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
<script lang="ts">
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
import Translations from "../../i18n/Translations"
|
||||
import Tr from "../../Base/Tr.svelte"
|
||||
import { InformationCircleIcon, TrashIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import type { OsmId, OsmTags } from "../../../Models/OsmFeature"
|
||||
import DeleteConfig from "../../../Models/ThemeConfig/DeleteConfig"
|
||||
import TagRenderingQuestion from "../TagRendering/TagRenderingQuestion.svelte"
|
||||
import type { Feature } from "geojson"
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource"
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils"
|
||||
import OsmChangeAction from "../../../Logic/Osm/Actions/OsmChangeAction"
|
||||
import DeleteAction from "../../../Logic/Osm/Actions/DeleteAction"
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"
|
||||
import Loading from "../../Base/Loading.svelte"
|
||||
import { DeleteFlowState } from "./DeleteFlowState"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import LoginToggle from "../../Base/LoginToggle.svelte";
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization";
|
||||
import Translations from "../../i18n/Translations";
|
||||
import Tr from "../../Base/Tr.svelte";
|
||||
import { TrashIcon } from "@babeard/svelte-heroicons/mini";
|
||||
import type { OsmId, OsmTags } from "../../../Models/OsmFeature";
|
||||
import DeleteConfig from "../../../Models/ThemeConfig/DeleteConfig";
|
||||
import TagRenderingQuestion from "../TagRendering/TagRenderingQuestion.svelte";
|
||||
import type { Feature } from "geojson";
|
||||
import { UIEventSource } from "../../../Logic/UIEventSource";
|
||||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig";
|
||||
import { TagsFilter } from "../../../Logic/Tags/TagsFilter";
|
||||
import { XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
import { TagUtils } from "../../../Logic/Tags/TagUtils";
|
||||
import OsmChangeAction from "../../../Logic/Osm/Actions/OsmChangeAction";
|
||||
import DeleteAction from "../../../Logic/Osm/Actions/DeleteAction";
|
||||
import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction";
|
||||
import Loading from "../../Base/Loading.svelte";
|
||||
import { DeleteFlowState } from "./DeleteFlowState";
|
||||
import { twJoin } from "tailwind-merge";
|
||||
|
||||
export let state: SpecialVisualizationState
|
||||
export let deleteConfig: DeleteConfig
|
||||
|
@ -83,10 +83,9 @@
|
|||
</script>
|
||||
|
||||
{#if $canBeDeleted === false && !hasSoftDeletion}
|
||||
<div class="low-interaction flex">
|
||||
<InformationCircleIcon class="h-6 w-6" />
|
||||
<div class="low-interaction flex flex-col">
|
||||
<Tr t={$canBeDeletedReason} />
|
||||
<Tr class="subtle" t={t.useSomethingElse} />
|
||||
<Tr cls="subtle" t={t.useSomethingElse} />
|
||||
</div>
|
||||
{:else}
|
||||
<LoginToggle ignoreLoading={true} {state}>
|
||||
|
|
|
@ -38,11 +38,12 @@
|
|||
let selectedMapping: number = undefined
|
||||
let checkedMappings: boolean[]
|
||||
$: {
|
||||
let tgs = $tags
|
||||
mappings = config.mappings?.filter((m) => {
|
||||
if (typeof m.hideInAnswer === "boolean") {
|
||||
return !m.hideInAnswer
|
||||
}
|
||||
return m.hideInAnswer.matchesProperties(tags.data)
|
||||
return !m.hideInAnswer.matchesProperties(tgs)
|
||||
})
|
||||
// We received a new config -> reinit
|
||||
unit = layer?.units?.find((unit) => unit.appliesToKeys.has(config.freeform?.key))
|
||||
|
@ -59,7 +60,7 @@
|
|||
if (config.freeform?.key) {
|
||||
if (!config.multiAnswer) {
|
||||
// Somehow, setting multianswer freeform values is broken if this is not set
|
||||
freeformInput.setData(tags.data[config.freeform.key])
|
||||
freeformInput.setData(tgs[config.freeform.key])
|
||||
}
|
||||
} else {
|
||||
freeformInput.setData(undefined)
|
||||
|
@ -69,7 +70,7 @@
|
|||
export let selectedTags: TagsFilter = undefined
|
||||
|
||||
let mappings: Mapping[] = config?.mappings
|
||||
let searchTerm: Store<string> = new UIEventSource("")
|
||||
let searchTerm: UIEventSource<string> = new UIEventSource("")
|
||||
|
||||
$: {
|
||||
try {
|
||||
|
|
|
@ -6,6 +6,7 @@ import Translations from "./i18n/Translations"
|
|||
import { QueryParameters } from "../Logic/Web/QueryParameters"
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
|
||||
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
|
||||
import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor"
|
||||
|
||||
export default class QueryParameterDocumentation {
|
||||
private static QueryParamDocsIntro = [
|
||||
|
@ -60,6 +61,7 @@ export default class QueryParameterDocumentation {
|
|||
public static GenerateQueryParameterDocs(): BaseUIElement {
|
||||
const docs: (string | BaseUIElement)[] = [
|
||||
...QueryParameterDocumentation.QueryParamDocsIntro,
|
||||
...ThemeViewStateHashActor.documentation,
|
||||
]
|
||||
this.UrlParamDocs().forEach((value, key) => {
|
||||
const c = new Combine([
|
||||
|
|
|
@ -82,6 +82,7 @@ import ConflateImportButtonViz from "./Popup/ImportButtons/ConflateImportButtonV
|
|||
import DeleteWizard from "./Popup/DeleteFlow/DeleteWizard.svelte"
|
||||
import { OpenJosm } from "./BigComponents/OpenJosm"
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
|
||||
import FediverseValidator from "./InputElement/Validators/FediverseValidator"
|
||||
|
||||
class NearbyImageVis implements SpecialVisualization {
|
||||
// Class must be in SpecialVisualisations due to weird cyclical import that breaks the tests
|
||||
|
@ -1374,6 +1375,43 @@ 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<Record<string, string>>,
|
||||
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 Link(
|
||||
fediAccount,
|
||||
"https://" + host + "/@" + username,
|
||||
true
|
||||
)
|
||||
})
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
specialVisualizations.push(new AutoApplyButton(specialVisualizations))
|
||||
|
|
|
@ -1,57 +1,57 @@
|
|||
<script lang="ts">
|
||||
import { Store, UIEventSource } from "../Logic/UIEventSource"
|
||||
import { Map as MlMap } from "maplibre-gl"
|
||||
import MaplibreMap from "./Map/MaplibreMap.svelte"
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState"
|
||||
import MapControlButton from "./Base/MapControlButton.svelte"
|
||||
import ToSvelte from "./Base/ToSvelte.svelte"
|
||||
import If from "./Base/If.svelte"
|
||||
import { GeolocationControl } from "./BigComponents/GeolocationControl"
|
||||
import type { Feature } from "geojson"
|
||||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte"
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
|
||||
import Filterview from "./BigComponents/Filterview.svelte"
|
||||
import ThemeViewState from "../Models/ThemeViewState"
|
||||
import type { MapProperties } from "../Models/MapProperties"
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte"
|
||||
import Translations from "./i18n/Translations"
|
||||
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Store, UIEventSource } from "../Logic/UIEventSource";
|
||||
import { Map as MlMap } from "maplibre-gl";
|
||||
import MaplibreMap from "./Map/MaplibreMap.svelte";
|
||||
import FeatureSwitchState from "../Logic/State/FeatureSwitchState";
|
||||
import MapControlButton from "./Base/MapControlButton.svelte";
|
||||
import ToSvelte from "./Base/ToSvelte.svelte";
|
||||
import If from "./Base/If.svelte";
|
||||
import { GeolocationControl } from "./BigComponents/GeolocationControl";
|
||||
import type { Feature } from "geojson";
|
||||
import SelectedElementView from "./BigComponents/SelectedElementView.svelte";
|
||||
import LayerConfig from "../Models/ThemeConfig/LayerConfig";
|
||||
import Filterview from "./BigComponents/Filterview.svelte";
|
||||
import ThemeViewState from "../Models/ThemeViewState";
|
||||
import type { MapProperties } from "../Models/MapProperties";
|
||||
import Geosearch from "./BigComponents/Geosearch.svelte";
|
||||
import Translations from "./i18n/Translations";
|
||||
import { CogIcon, EyeIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid";
|
||||
|
||||
import Tr from "./Base/Tr.svelte"
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
|
||||
import FloatOver from "./Base/FloatOver.svelte"
|
||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
|
||||
import Constants from "../Models/Constants"
|
||||
import TabbedGroup from "./Base/TabbedGroup.svelte"
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState"
|
||||
import LoginToggle from "./Base/LoginToggle.svelte"
|
||||
import LoginButton from "./Base/LoginButton.svelte"
|
||||
import CopyrightPanel from "./BigComponents/CopyrightPanel"
|
||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
|
||||
import ModalRight from "./Base/ModalRight.svelte"
|
||||
import { Utils } from "../Utils"
|
||||
import Hotkeys from "./Base/Hotkeys"
|
||||
import { VariableUiElement } from "./Base/VariableUIElement"
|
||||
import SvelteUIElement from "./Base/SvelteUIElement"
|
||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte"
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte"
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton"
|
||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte"
|
||||
import Svg from "../Svg"
|
||||
import { ShareScreen } from "./BigComponents/ShareScreen"
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
|
||||
import type { RasterLayerPolygon } from "../Models/RasterLayers"
|
||||
import { AvailableRasterLayers } from "../Models/RasterLayers"
|
||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
|
||||
import IfHidden from "./Base/IfHidden.svelte"
|
||||
import { onDestroy } from "svelte"
|
||||
import { OpenJosm } from "./BigComponents/OpenJosm"
|
||||
import MapillaryLink from "./BigComponents/MapillaryLink.svelte"
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte"
|
||||
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
|
||||
import StateIndicator from "./BigComponents/StateIndicator.svelte"
|
||||
import LanguagePicker from "./LanguagePicker"
|
||||
import Locale from "./i18n/Locale"
|
||||
import Tr from "./Base/Tr.svelte";
|
||||
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
|
||||
import FloatOver from "./Base/FloatOver.svelte";
|
||||
import PrivacyPolicy from "./BigComponents/PrivacyPolicy";
|
||||
import Constants from "../Models/Constants";
|
||||
import TabbedGroup from "./Base/TabbedGroup.svelte";
|
||||
import UserRelatedState from "../Logic/State/UserRelatedState";
|
||||
import LoginToggle from "./Base/LoginToggle.svelte";
|
||||
import LoginButton from "./Base/LoginButton.svelte";
|
||||
import CopyrightPanel from "./BigComponents/CopyrightPanel";
|
||||
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte";
|
||||
import ModalRight from "./Base/ModalRight.svelte";
|
||||
import { Utils } from "../Utils";
|
||||
import Hotkeys from "./Base/Hotkeys";
|
||||
import { VariableUiElement } from "./Base/VariableUIElement";
|
||||
import SvelteUIElement from "./Base/SvelteUIElement";
|
||||
import OverlayToggle from "./BigComponents/OverlayToggle.svelte";
|
||||
import LevelSelector from "./BigComponents/LevelSelector.svelte";
|
||||
import ExtraLinkButton from "./BigComponents/ExtraLinkButton";
|
||||
import SelectedElementTitle from "./BigComponents/SelectedElementTitle.svelte";
|
||||
import Svg from "../Svg";
|
||||
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte";
|
||||
import type { RasterLayerPolygon } from "../Models/RasterLayers";
|
||||
import { AvailableRasterLayers } from "../Models/RasterLayers";
|
||||
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte";
|
||||
import IfHidden from "./Base/IfHidden.svelte";
|
||||
import { onDestroy } from "svelte";
|
||||
import { OpenJosm } from "./BigComponents/OpenJosm";
|
||||
import MapillaryLink from "./BigComponents/MapillaryLink.svelte";
|
||||
import OpenIdEditor from "./BigComponents/OpenIdEditor.svelte";
|
||||
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte";
|
||||
import StateIndicator from "./BigComponents/StateIndicator.svelte";
|
||||
import LanguagePicker from "./LanguagePicker";
|
||||
import Locale from "./i18n/Locale";
|
||||
import ShareScreen from "./BigComponents/ShareScreen.svelte";
|
||||
|
||||
export let state: ThemeViewState
|
||||
let layout = state.layout
|
||||
|
@ -314,11 +314,12 @@
|
|||
|
||||
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3" />
|
||||
|
||||
<div slot="title4">
|
||||
<div slot="title4" class="flex">
|
||||
<ToSvelte construct={Svg.share_svg().SetClass("w-4 h-4")} />
|
||||
<Tr t={Translations.t.general.sharescreen.title} />
|
||||
</div>
|
||||
<div class="m-2" slot="content4">
|
||||
<ToSvelte construct={() => new ShareScreen(state)} />
|
||||
<ShareScreen {state}/>
|
||||
</div>
|
||||
</TabbedGroup>
|
||||
</FloatOver>
|
||||
|
|
19
src/Utils.ts
19
src/Utils.ts
|
@ -221,6 +221,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
* Utils.Round7(12.123456789) // => 12.1234568
|
||||
*/
|
||||
public static Round7(i: number): number {
|
||||
if (i == undefined) {
|
||||
return undefined
|
||||
}
|
||||
return Math.round(i * 10000000) / 10000000
|
||||
}
|
||||
|
||||
|
@ -1211,6 +1214,22 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
return new Date(str)
|
||||
}
|
||||
|
||||
public static selectTextIn(node) {
|
||||
if (document.body["createTextRange"]) {
|
||||
const range = document.body["createTextRange"]()
|
||||
range.moveToElementText(node)
|
||||
range.select()
|
||||
} else if (window.getSelection) {
|
||||
const selection = window.getSelection()
|
||||
const range = document.createRange()
|
||||
range.selectNodeContents(node)
|
||||
selection.removeAllRanges()
|
||||
selection.addRange(range)
|
||||
} else {
|
||||
console.warn("Could not select text in node: Unsupported browser.")
|
||||
}
|
||||
}
|
||||
|
||||
public static sortedByLevenshteinDistance<T>(
|
||||
reference: string,
|
||||
ts: T[],
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue