forked from MapComplete/MapComplete
		
	Move various tabs into buttons, more work on a11y
This commit is contained in:
		
							parent
							
								
									cce9b879f2
								
							
						
					
					
						commit
						7e852dd7e3
					
				
					 29 changed files with 10642 additions and 10432 deletions
				
			
		
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
					@ -463,6 +463,7 @@
 | 
				
			||||||
        "key": "Key combination",
 | 
					        "key": "Key combination",
 | 
				
			||||||
        "openFilterPanel": "Opens the POI-layers and filter panel",
 | 
					        "openFilterPanel": "Opens the POI-layers and filter panel",
 | 
				
			||||||
        "openLayersPanel": "Opens the background layers panel",
 | 
					        "openLayersPanel": "Opens the background layers panel",
 | 
				
			||||||
 | 
					        "queryCurrentLocation": "Display the address which is nearest the map center",
 | 
				
			||||||
        "selectAerial": "Set the background to aerial or satellite imagery. Toggles between the two best, available layers",
 | 
					        "selectAerial": "Set the background to aerial or satellite imagery. Toggles between the two best, available layers",
 | 
				
			||||||
        "selectFavourites": "Open the favourites page",
 | 
					        "selectFavourites": "Open the favourites page",
 | 
				
			||||||
        "selectItem": "Select the POI which is closest to the map center (crosshair). Only when in keyboard navigation is used",
 | 
					        "selectItem": "Select the POI which is closest to the map center (crosshair). Only when in keyboard navigation is used",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2000,19 +2000,6 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "tagRenderings": {
 | 
					        "tagRenderings": {
 | 
				
			||||||
      "rewritten-questions": {
 | 
					 | 
				
			||||||
        "renderings": {
 | 
					 | 
				
			||||||
          "0": {
 | 
					 | 
				
			||||||
            "question": "Wie viele Stecker vom Typ {{description}} sind hier vorhanden?"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "1": {
 | 
					 | 
				
			||||||
            "mappings": {
 | 
					 | 
				
			||||||
              "then": "{{description}} liefert {{commonVoltages}} Volt"
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "question": "Welche Spannung liefern die {{description}}?"
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
            "Authentication": {
 | 
					            "Authentication": {
 | 
				
			||||||
                "mappings": {
 | 
					                "mappings": {
 | 
				
			||||||
                    "1": {
 | 
					                    "1": {
 | 
				
			||||||
| 
						 | 
					@ -2293,6 +2280,19 @@
 | 
				
			||||||
                "question": "Welche Kennnummer hat die Ladestation?",
 | 
					                "question": "Welche Kennnummer hat die Ladestation?",
 | 
				
			||||||
                "render": "Die Kennziffer ist <b>{ref}</b>"
 | 
					                "render": "Die Kennziffer ist <b>{ref}</b>"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            "rewritten-questions": {
 | 
				
			||||||
 | 
					                "renderings": {
 | 
				
			||||||
 | 
					                    "0": {
 | 
				
			||||||
 | 
					                        "question": "Wie viele Stecker vom Typ {{description}} sind hier vorhanden?"
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    "1": {
 | 
				
			||||||
 | 
					                        "mappings": {
 | 
				
			||||||
 | 
					                            "then": "{{description}} liefert {{commonVoltages}} Volt"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "question": "Welche Spannung liefern die {{description}}?"
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            "website": {
 | 
					            "website": {
 | 
				
			||||||
                "question": "Auf welcher Webseite kann man weitere Informationen über diese Ladestation finden?",
 | 
					                "question": "Auf welcher Webseite kann man weitere Informationen über diese Ladestation finden?",
 | 
				
			||||||
                "render": "Weitere Informationen unter <a href='{website}'>{website}</a>"
 | 
					                "render": "Weitere Informationen unter <a href='{website}'>{website}</a>"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7154,6 +7154,7 @@
 | 
				
			||||||
                "question": "Are dogs allowed in this business?"
 | 
					                "question": "Are dogs allowed in this business?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "email": {
 | 
					            "email": {
 | 
				
			||||||
 | 
					                "editButtonAriaLabel": "Edit email address",
 | 
				
			||||||
                "question": "What is the email address of {title()}?"
 | 
					                "question": "What is the email address of {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "gluten_free": {
 | 
					            "gluten_free": {
 | 
				
			||||||
| 
						 | 
					@ -7352,6 +7353,7 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "phone": {
 | 
					            "phone": {
 | 
				
			||||||
 | 
					                "editButtonAriaLabel": "Edit phone number",
 | 
				
			||||||
                "question": "What is the phone number of {title()}?"
 | 
					                "question": "What is the phone number of {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "repeated": {
 | 
					            "repeated": {
 | 
				
			||||||
| 
						 | 
					@ -7454,6 +7456,7 @@
 | 
				
			||||||
                "question": "Does this place offer a vegan option?"
 | 
					                "question": "Does this place offer a vegan option?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "website": {
 | 
					            "website": {
 | 
				
			||||||
 | 
					                "editButtonAriaLabel": "Edit website",
 | 
				
			||||||
                "question": "What is the website of {title()}?"
 | 
					                "question": "What is the website of {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "wheelchair-access": {
 | 
					            "wheelchair-access": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6338,6 +6338,7 @@
 | 
				
			||||||
                "question": "Zijn honden toegelaten in deze zaak?"
 | 
					                "question": "Zijn honden toegelaten in deze zaak?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "email": {
 | 
					            "email": {
 | 
				
			||||||
 | 
					                "editButtonAriaLabel": "Pas emailadres aan",
 | 
				
			||||||
                "question": "Wat is het e-mailadres van {title()}?"
 | 
					                "question": "Wat is het e-mailadres van {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "induction-loop": {
 | 
					            "induction-loop": {
 | 
				
			||||||
| 
						 | 
					@ -6482,6 +6483,7 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "phone": {
 | 
					            "phone": {
 | 
				
			||||||
 | 
					                "editButtonAriaLabel": "Pas telefoonnummer aan",
 | 
				
			||||||
                "question": "Wat is het telefoonnummer van {title()}?"
 | 
					                "question": "Wat is het telefoonnummer van {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "repeated": {
 | 
					            "repeated": {
 | 
				
			||||||
| 
						 | 
					@ -6540,6 +6542,7 @@
 | 
				
			||||||
                "question": "Is roken toegestaan bij {title()}?"
 | 
					                "question": "Is roken toegestaan bij {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "website": {
 | 
					            "website": {
 | 
				
			||||||
 | 
					                "editButtonAriaLabel": "Pas website aan",
 | 
				
			||||||
                "question": "Wat is de website van {title()}?"
 | 
					                "question": "Wat is de website van {title()}?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "wheelchair-access": {
 | 
					            "wheelchair-access": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "name": "mapcomplete",
 | 
					  "name": "mapcomplete",
 | 
				
			||||||
  "version": "0.36.6",
 | 
					  "version": "0.36.7",
 | 
				
			||||||
  "repository": "https://github.com/pietervdvn/MapComplete",
 | 
					  "repository": "https://github.com/pietervdvn/MapComplete",
 | 
				
			||||||
  "description": "A small website to edit OSM easily",
 | 
					  "description": "A small website to edit OSM easily",
 | 
				
			||||||
  "bugs": "https://github.com/pietervdvn/MapComplete/issues",
 | 
					  "bugs": "https://github.com/pietervdvn/MapComplete/issues",
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@
 | 
				
			||||||
      "https://overpass.openstreetmap.ru/cgi/interpreter"
 | 
					      "https://overpass.openstreetmap.ru/cgi/interpreter"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "country_coder_host": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/latlon2country",
 | 
					    "country_coder_host": "https://raw.githubusercontent.com/pietervdvn/MapComplete-data/main/latlon2country",
 | 
				
			||||||
    "nominatimEndpoint": "https://nominatim.openstreetmap.org/"
 | 
					    "nominatimEndpoint": "https://geocoding.geofabrik.de/b75350b1cfc34962ac49824fe5b582dc/"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "start": "npm run generate:layeroverview && npm run strt",
 | 
					    "start": "npm run generate:layeroverview && npm run strt",
 | 
				
			||||||
| 
						 | 
					@ -75,7 +75,7 @@
 | 
				
			||||||
    "lint:prettier": "prettier  --check '**/*.ts' '**/*.svelte'",
 | 
					    "lint:prettier": "prettier  --check '**/*.ts' '**/*.svelte'",
 | 
				
			||||||
    "format": "prettier --write '**/*.ts' '**/*.svelte'",
 | 
					    "format": "prettier --write '**/*.ts' '**/*.svelte'",
 | 
				
			||||||
    "clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm",
 | 
					    "clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm",
 | 
				
			||||||
    "clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|test\\|studio\\|theme\\|style_test\\|statistics\\|leaderboard\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)",
 | 
					    "clean": "rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|privacy\\|test\\|studio\\|theme\\|style_test\\|statistics\\|leaderboard\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)",
 | 
				
			||||||
    "generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
 | 
					    "generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
 | 
				
			||||||
    "weblate-add-upstream": "git remote add weblate-github git@github.com:weblate/MapComplete.git && git remote add weblate-hosted-core https://hosted.weblate.org/git/mapcomplete/core/ && git remote add weblate-hosted-layers https://hosted.weblate.org/git/mapcomplete/layers/",
 | 
					    "weblate-add-upstream": "git remote add weblate-github git@github.com:weblate/MapComplete.git && git remote add weblate-hosted-core https://hosted.weblate.org/git/mapcomplete/core/ && git remote add weblate-hosted-layers https://hosted.weblate.org/git/mapcomplete/layers/",
 | 
				
			||||||
    "weblate-merge": "git remote update weblate-github; git merge weblate-github/weblate-mapcomplete-core weblate-github/weblate-mapcomplete-layers weblate-github/weblate-mapcomplete-layer-translations",
 | 
					    "weblate-merge": "git remote update weblate-github; git merge weblate-github/weblate-mapcomplete-core weblate-github/weblate-mapcomplete-layers weblate-github/weblate-mapcomplete-layer-translations",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1071,14 +1071,14 @@ video {
 | 
				
			||||||
  height: 6rem;
 | 
					  height: 6rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h-screen {
 | 
					 | 
				
			||||||
  height: 100vh;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.h-full {
 | 
					.h-full {
 | 
				
			||||||
  height: 100%;
 | 
					  height: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.h-screen {
 | 
				
			||||||
 | 
					  height: 100vh;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.h-32 {
 | 
					.h-32 {
 | 
				
			||||||
  height: 8rem;
 | 
					  height: 8rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1684,6 +1684,11 @@ video {
 | 
				
			||||||
  border-color: rgb(0 0 0 / var(--tw-border-opacity));
 | 
					  border-color: rgb(0 0 0 / var(--tw-border-opacity));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.border-gray-500 {
 | 
				
			||||||
 | 
					  --tw-border-opacity: 1;
 | 
				
			||||||
 | 
					  border-color: rgb(107 114 128 / var(--tw-border-opacity));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.border-subtle {
 | 
					.border-subtle {
 | 
				
			||||||
  --tw-border-opacity: 1;
 | 
					  --tw-border-opacity: 1;
 | 
				
			||||||
  border-color: rgb(219 234 254 / var(--tw-border-opacity));
 | 
					  border-color: rgb(219 234 254 / var(--tw-border-opacity));
 | 
				
			||||||
| 
						 | 
					@ -1709,11 +1714,6 @@ video {
 | 
				
			||||||
  border-color: rgb(156 163 175 / var(--tw-border-opacity));
 | 
					  border-color: rgb(156 163 175 / var(--tw-border-opacity));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.border-gray-500 {
 | 
					 | 
				
			||||||
  --tw-border-opacity: 1;
 | 
					 | 
				
			||||||
  border-color: rgb(107 114 128 / var(--tw-border-opacity));
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.border-gray-800 {
 | 
					.border-gray-800 {
 | 
				
			||||||
  --tw-border-opacity: 1;
 | 
					  --tw-border-opacity: 1;
 | 
				
			||||||
  border-color: rgb(31 41 55 / var(--tw-border-opacity));
 | 
					  border-color: rgb(31 41 55 / var(--tw-border-opacity));
 | 
				
			||||||
| 
						 | 
					@ -1770,14 +1770,14 @@ video {
 | 
				
			||||||
  padding: 2rem;
 | 
					  padding: 2rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.p-2 {
 | 
					 | 
				
			||||||
  padding: 0.5rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.p-4 {
 | 
					.p-4 {
 | 
				
			||||||
  padding: 1rem;
 | 
					  padding: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.p-2 {
 | 
				
			||||||
 | 
					  padding: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.p-1 {
 | 
					.p-1 {
 | 
				
			||||||
  padding: 0.25rem;
 | 
					  padding: 0.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1814,6 +1814,10 @@ video {
 | 
				
			||||||
  padding-right: 0.75rem;
 | 
					  padding-right: 0.75rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.pr-2 {
 | 
				
			||||||
 | 
					  padding-right: 0.5rem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pr-12 {
 | 
					.pr-12 {
 | 
				
			||||||
  padding-right: 3rem;
 | 
					  padding-right: 3rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1822,8 +1826,8 @@ video {
 | 
				
			||||||
  padding-left: 0.25rem;
 | 
					  padding-left: 0.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pr-2 {
 | 
					.pr-1 {
 | 
				
			||||||
  padding-right: 0.5rem;
 | 
					  padding-right: 0.25rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pl-2 {
 | 
					.pl-2 {
 | 
				
			||||||
| 
						 | 
					@ -1854,10 +1858,6 @@ video {
 | 
				
			||||||
  padding-left: 1rem;
 | 
					  padding-left: 1rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.pr-1 {
 | 
					 | 
				
			||||||
  padding-right: 0.25rem;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.pl-3 {
 | 
					.pl-3 {
 | 
				
			||||||
  padding-left: 0.75rem;
 | 
					  padding-left: 0.75rem;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2509,6 +2509,11 @@ label.checked:not(.neutral-label) {
 | 
				
			||||||
  width: 100%;
 | 
					  width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button.soft, .button.soft {
 | 
				
			||||||
 | 
					  border: 2px solid var(--interactive-background);
 | 
				
			||||||
 | 
					  margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.links-as-button a {
 | 
					.links-as-button a {
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
    * Let a 'link' mimick a button, but not entirely
 | 
					    * Let a 'link' mimick a button, but not entirely
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ export class BBox {
 | 
				
			||||||
     * Coordinates should be [[lon, lat],[lon, lat]]
 | 
					     * Coordinates should be [[lon, lat],[lon, lat]]
 | 
				
			||||||
     * @param coordinates
 | 
					     * @param coordinates
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    constructor(coordinates) {
 | 
					    constructor(coordinates: [number,number][]) {
 | 
				
			||||||
        this.maxLat = -90
 | 
					        this.maxLat = -90
 | 
				
			||||||
        this.maxLon = -180
 | 
					        this.maxLon = -180
 | 
				
			||||||
        this.minLat = 90
 | 
					        this.minLat = 90
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,13 +29,14 @@ export class Geocoding {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static async reverse(
 | 
					    static async reverse(
 | 
				
			||||||
        coordinate: { lon: number; lat: number },
 | 
					        coordinate: { lon: number; lat: number },
 | 
				
			||||||
        zoom: number = 18
 | 
					        zoom: number = 17,
 | 
				
			||||||
 | 
					        language?: string
 | 
				
			||||||
    ): Promise<FeatureCollection> {
 | 
					    ): Promise<FeatureCollection> {
 | 
				
			||||||
        // https://nominatim.org/release-docs/develop/api/Reverse/
 | 
					        // https://nominatim.org/release-docs/develop/api/Reverse/
 | 
				
			||||||
        // IF the zoom is low, it'll only return a country instead of an address
 | 
					        // IF the zoom is low, it'll only return a country instead of an address
 | 
				
			||||||
        const url = `${Geocoding.host}reverse?format=geojson&lat=${coordinate.lat}&lon=${
 | 
					        const url = `${Geocoding.host}reverse?format=geojson&lat=${coordinate.lat}&lon=${
 | 
				
			||||||
            coordinate.lon
 | 
					            coordinate.lon
 | 
				
			||||||
        }&zoom=${Math.round(zoom)}`
 | 
					        }&zoom=${Math.ceil(zoom) + 1}&accept-language=${language}`
 | 
				
			||||||
        return Utils.downloadJson(url)
 | 
					        return Utils.downloadJson(url)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,40 +3,12 @@ import { Utils } from "../../Utils"
 | 
				
			||||||
export class ThemeMetaTagging {
 | 
					export class ThemeMetaTagging {
 | 
				
			||||||
   public static readonly themeName = "usersettings"
 | 
					   public static readonly themeName = "usersettings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public metaTaggging_for_usersettings(feat: { properties: Record<string, string> }) {
 | 
					   public metaTaggging_for_usersettings(feat: {properties: Record<string, string>}) {
 | 
				
			||||||
        Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
 | 
					      Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) ) 
 | 
				
			||||||
            feat.properties._description
 | 
					      Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' ) 
 | 
				
			||||||
                .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
 | 
					      Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href   }) (feat)  ) 
 | 
				
			||||||
                ?.at(1)
 | 
					      Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat)  ) 
 | 
				
			||||||
        )
 | 
					      Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a ) 
 | 
				
			||||||
        Utils.AddLazyProperty(
 | 
					      feat.properties['__current_backgroun'] = 'initial_value'
 | 
				
			||||||
            feat.properties,
 | 
					 | 
				
			||||||
            "_d",
 | 
					 | 
				
			||||||
            () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
 | 
					 | 
				
			||||||
            ((feat) => {
 | 
					 | 
				
			||||||
                const e = document.createElement("div")
 | 
					 | 
				
			||||||
                e.innerHTML = feat.properties._d
 | 
					 | 
				
			||||||
                return Array.from(e.getElementsByTagName("a")).filter(
 | 
					 | 
				
			||||||
                    (a) => a.href.match(/mastodon|en.osm.town/) !== null
 | 
					 | 
				
			||||||
                )[0]?.href
 | 
					 | 
				
			||||||
            })(feat)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
 | 
					 | 
				
			||||||
            ((feat) => {
 | 
					 | 
				
			||||||
                const e = document.createElement("div")
 | 
					 | 
				
			||||||
                e.innerHTML = feat.properties._d
 | 
					 | 
				
			||||||
                return Array.from(e.getElementsByTagName("a")).filter(
 | 
					 | 
				
			||||||
                    (a) => a.getAttribute("rel")?.indexOf("me") >= 0
 | 
					 | 
				
			||||||
                )[0]?.href
 | 
					 | 
				
			||||||
            })(feat)
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        Utils.AddLazyProperty(
 | 
					 | 
				
			||||||
            feat.properties,
 | 
					 | 
				
			||||||
            "_mastodon_candidate",
 | 
					 | 
				
			||||||
            () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
        feat.properties["__current_backgroun"] = "initial_value"
 | 
					 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ export class MenuState {
 | 
				
			||||||
        new UIEventSource<boolean>(false)
 | 
					        new UIEventSource<boolean>(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public readonly filtersPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
 | 
					    public readonly filtersPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
 | 
				
			||||||
 | 
					    public readonly privacyPanelIsOpened: UIEventSource<boolean> = new UIEventSource<boolean>(false)
 | 
				
			||||||
 | 
					    public readonly communityIndexPanelIsOpened: UIEventSource<boolean> = new UIEventSource(false)
 | 
				
			||||||
    public readonly allToggles: {
 | 
					    public readonly allToggles: {
 | 
				
			||||||
        toggle: UIEventSource<boolean>
 | 
					        toggle: UIEventSource<boolean>
 | 
				
			||||||
        name: string
 | 
					        name: string
 | 
				
			||||||
| 
						 | 
					@ -151,13 +152,21 @@ export class MenuState {
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public closeAll(): boolean {
 | 
					    public closeAll(): boolean {
 | 
				
			||||||
        const toggles = [
 | 
					        const toggles = [
 | 
				
			||||||
            this.menuIsOpened,
 | 
					            this.communityIndexPanelIsOpened,
 | 
				
			||||||
            this.themeIsOpened,
 | 
					            this.privacyPanelIsOpened,
 | 
				
			||||||
            this.backgroundLayerSelectionIsOpened,
 | 
					            this.backgroundLayerSelectionIsOpened,
 | 
				
			||||||
            this.filtersPanelIsOpened,
 | 
					            this.filtersPanelIsOpened,
 | 
				
			||||||
 | 
					            this.menuIsOpened,
 | 
				
			||||||
 | 
					            this.themeIsOpened,
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        const somethingIsOpen = toggles.some((t) => t.data)
 | 
					        let somethingIsOpen = false
 | 
				
			||||||
        toggles.forEach((t) => t.setData(false))
 | 
					        for (const t of toggles) {
 | 
				
			||||||
 | 
					            somethingIsOpen = t.data
 | 
				
			||||||
 | 
					            t.setData(false)
 | 
				
			||||||
 | 
					            if (somethingIsOpen) {
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return somethingIsOpen
 | 
					        return somethingIsOpen
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -487,10 +487,8 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private initHotkeys() {
 | 
					    private initHotkeys() {
 | 
				
			||||||
        Hotkeys.RegisterHotkey(
 | 
					        const docs = Translations.t.hotkeyDocumentation
 | 
				
			||||||
            { nomod: "Escape", onUp: true },
 | 
					        Hotkeys.RegisterHotkey({ nomod: "Escape", onUp: true }, docs.closeSidebar, () => {
 | 
				
			||||||
            Translations.t.hotkeyDocumentation.closeSidebar,
 | 
					 | 
				
			||||||
            () => {
 | 
					 | 
				
			||||||
            if (this.previewedImage.data !== undefined) {
 | 
					            if (this.previewedImage.data !== undefined) {
 | 
				
			||||||
                this.previewedImage.setData(undefined)
 | 
					                this.previewedImage.setData(undefined)
 | 
				
			||||||
                return
 | 
					                return
 | 
				
			||||||
| 
						 | 
					@ -498,24 +496,19 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
				
			||||||
            this.selectedElement.setData(undefined)
 | 
					            this.selectedElement.setData(undefined)
 | 
				
			||||||
            this.guistate.closeAll()
 | 
					            this.guistate.closeAll()
 | 
				
			||||||
            this.focusOnMap()
 | 
					            this.focusOnMap()
 | 
				
			||||||
            }
 | 
					        })
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Hotkeys.RegisterHotkey(
 | 
					        Hotkeys.RegisterHotkey({ nomod: "f" }, docs.selectFavourites, () => {
 | 
				
			||||||
            { nomod: "f" },
 | 
					 | 
				
			||||||
            Translations.t.hotkeyDocumentation.selectFavourites,
 | 
					 | 
				
			||||||
            () => {
 | 
					 | 
				
			||||||
            this.guistate.menuViewTab.setData("favourites")
 | 
					            this.guistate.menuViewTab.setData("favourites")
 | 
				
			||||||
            this.guistate.menuIsOpened.setData(true)
 | 
					            this.guistate.menuIsOpened.setData(true)
 | 
				
			||||||
            }
 | 
					        })
 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Hotkeys.RegisterHotkey(
 | 
					        Hotkeys.RegisterHotkey(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                nomod: " ",
 | 
					                nomod: " ",
 | 
				
			||||||
                onUp: true,
 | 
					                onUp: true,
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            Translations.t.hotkeyDocumentation.selectItem,
 | 
					            docs.selectItem,
 | 
				
			||||||
            () => {
 | 
					            () => {
 | 
				
			||||||
                if (this.selectedElement.data !== undefined) {
 | 
					                if (this.selectedElement.data !== undefined) {
 | 
				
			||||||
                    return false
 | 
					                    return false
 | 
				
			||||||
| 
						 | 
					@ -537,7 +530,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
				
			||||||
                    nomod: "" + i,
 | 
					                    nomod: "" + i,
 | 
				
			||||||
                    onUp: true,
 | 
					                    onUp: true,
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                Translations.t.hotkeyDocumentation.selectItem,
 | 
					                docs.selectItem,
 | 
				
			||||||
                () => this.selectClosestAtCenter(i - 1)
 | 
					                () => this.selectClosestAtCenter(i - 1)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -550,7 +543,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    nomod: "b",
 | 
					                    nomod: "b",
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                Translations.t.hotkeyDocumentation.openLayersPanel,
 | 
					                docs.openLayersPanel,
 | 
				
			||||||
                () => {
 | 
					                () => {
 | 
				
			||||||
                    if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
 | 
					                    if (this.featureSwitches.featureSwitchBackgroundSelection.data) {
 | 
				
			||||||
                        this.guistate.backgroundLayerSelectionIsOpened.setData(true)
 | 
					                        this.guistate.backgroundLayerSelectionIsOpened.setData(true)
 | 
				
			||||||
| 
						 | 
					@ -563,6 +556,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                Translations.t.hotkeyDocumentation.openFilterPanel,
 | 
					                Translations.t.hotkeyDocumentation.openFilterPanel,
 | 
				
			||||||
                () => {
 | 
					                () => {
 | 
				
			||||||
 | 
					                    console.log("S pressed")
 | 
				
			||||||
                    if (this.featureSwitches.featureSwitchFilter.data) {
 | 
					                    if (this.featureSwitches.featureSwitchFilter.data) {
 | 
				
			||||||
                        this.guistate.openFilterView()
 | 
					                        this.guistate.openFilterView()
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -646,7 +640,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
 | 
					        this.closestFeatures.registerSource(specialLayers.favourite, "favourite")
 | 
				
			||||||
        if (this.layout?.lockLocation) {
 | 
					        if (this.layout?.lockLocation) {
 | 
				
			||||||
            const bbox = new BBox(this.layout.lockLocation)
 | 
					            const bbox = new BBox(<any>this.layout.lockLocation)
 | 
				
			||||||
            this.mapProperties.maxbounds.setData(bbox)
 | 
					            this.mapProperties.maxbounds.setData(bbox)
 | 
				
			||||||
            ShowDataLayer.showRange(
 | 
					            ShowDataLayer.showRange(
 | 
				
			||||||
                this.map,
 | 
					                this.map,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@
 | 
				
			||||||
  import { LayoutInformation } from "../Models/ThemeConfig/LayoutConfig"
 | 
					  import { LayoutInformation } from "../Models/ThemeConfig/LayoutConfig"
 | 
				
			||||||
  import * as themeOverview from "../assets/generated/theme_overview.json"
 | 
					  import * as themeOverview from "../assets/generated/theme_overview.json"
 | 
				
			||||||
  import UnofficialThemeList from "./BigComponents/UnofficialThemeList.svelte"
 | 
					  import UnofficialThemeList from "./BigComponents/UnofficialThemeList.svelte"
 | 
				
			||||||
 | 
					  import Eye from "@babeard/svelte-heroicons/mini/Eye"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const featureSwitches = new OsmConnectionFeatureSwitches()
 | 
					  const featureSwitches = new OsmConnectionFeatureSwitches()
 | 
				
			||||||
  const osmConnection = new OsmConnection({
 | 
					  const osmConnection = new OsmConnection({
 | 
				
			||||||
| 
						 | 
					@ -136,6 +137,14 @@
 | 
				
			||||||
      <Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
 | 
					      <Tr t={Translations.t.general.morescreen.createYourOwnTheme} />
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <a
 | 
				
			||||||
 | 
					      class="button h-fit w-full"
 | 
				
			||||||
 | 
					      href={window.location.protocol + "//" + window.location.host + "/privacy.html"}
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Eye class="mr-2 h-6 w-6" />
 | 
				
			||||||
 | 
					      <Tr t={Translations.t.privacy.title} />
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  </LoginToggle>
 | 
					  </LoginToggle>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,7 +37,7 @@
 | 
				
			||||||
    <slot name="close-button">
 | 
					    <slot name="close-button">
 | 
				
			||||||
      <!-- The close button is placed _after_ the default slot in order to always paint it on top -->
 | 
					      <!-- The close button is placed _after_ the default slot in order to always paint it on top -->
 | 
				
			||||||
      <button
 | 
					      <button
 | 
				
			||||||
        class="absolute right-10 top-10 h-8 w-8 cursor-pointer border-none bg-white p-0"
 | 
					        class="absolute right-10 top-10 h-8 w-8 cursor-pointer border-none bg-white rounded-full p-0"
 | 
				
			||||||
        on:click={() => dispatch("close")}
 | 
					        on:click={() => dispatch("close")}
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <XCircleIcon />
 | 
					        <XCircleIcon />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@
 | 
				
			||||||
>
 | 
					>
 | 
				
			||||||
  <slot name="close-button">
 | 
					  <slot name="close-button">
 | 
				
			||||||
    <button
 | 
					    <button
 | 
				
			||||||
      class="absolute right-10 top-10 h-8 w-8 cursor-pointer"
 | 
					      class="absolute right-10 top-10 h-8 w-8 cursor-pointer rounded-full"
 | 
				
			||||||
      on:click={() => dispatch("close")}
 | 
					      on:click={() => dispatch("close")}
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <XCircleIcon />
 | 
					      <XCircleIcon />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
  import Josm_logo from "../../assets/svg/Josm_logo.svelte"
 | 
					  import Josm_logo from "../../assets/svg/Josm_logo.svelte"
 | 
				
			||||||
  import Constants from "../../Models/Constants"
 | 
					  import Constants from "../../Models/Constants"
 | 
				
			||||||
  import type { SpecialVisualizationState } from "../SpecialVisualization"
 | 
					  import type { SpecialVisualizationState } from "../SpecialVisualization"
 | 
				
			||||||
 | 
					  import { Utils } from "../../Utils"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let state: SpecialVisualizationState
 | 
					  export let state: SpecialVisualizationState
 | 
				
			||||||
  const t = Translations.t.general.attribution
 | 
					  const t = Translations.t.general.attribution
 | 
				
			||||||
| 
						 | 
					@ -13,7 +14,7 @@
 | 
				
			||||||
  josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined))
 | 
					  josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const showButton = state.osmConnection.userDetails.map(
 | 
					  const showButton = state.osmConnection.userDetails.map(
 | 
				
			||||||
    (ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
 | 
					    (ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible,
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  function openJosm() {
 | 
					  function openJosm() {
 | 
				
			||||||
| 
						 | 
					@ -33,16 +34,20 @@
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if $showButton}
 | 
					{#if $showButton}
 | 
				
			||||||
  {#if $josmState === undefined}
 | 
					  <div class="flex">
 | 
				
			||||||
    <!-- empty -->
 | 
					 | 
				
			||||||
  {:else if state === "OK"}
 | 
					 | 
				
			||||||
    <Tr cls="thanks" t={t.josmOpened} />
 | 
					 | 
				
			||||||
  {:else}
 | 
					 | 
				
			||||||
    <Tr cls="alert" t={t.josmNotOpened} />
 | 
					 | 
				
			||||||
  {/if}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <button class="flex items-center" on:click={openJosm}>
 | 
					    <button class="flex items-center small soft grow" on:click={openJosm}>
 | 
				
			||||||
    <Josm_logo class="h-12 w-12 p-2 pr-4" />
 | 
					      <Josm_logo class="h-6 w-6 pr-2" />
 | 
				
			||||||
      <Tr t={t.editJosm} />
 | 
					      <Tr t={t.editJosm} />
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    {#if $josmState === undefined}
 | 
				
			||||||
 | 
					      <!-- empty -->
 | 
				
			||||||
 | 
					    {:else if $josmState === "OK"}
 | 
				
			||||||
 | 
					      <Tr cls="thanks shrink-0 w-fit" t={t.josmOpened} />
 | 
				
			||||||
 | 
					    {:else}
 | 
				
			||||||
 | 
					      <Tr cls="alert shrink-0 w-fit" t={t.josmNotOpened} />
 | 
				
			||||||
 | 
					    {/if}
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<button on:click={share} class="secondary m-0 h-8 w-8 p-0" use:ariaLabel={Translations.t.general.share}>
 | 
					<button on:click={share} class="soft m-0 h-8 w-8 p-0" use:ariaLabel={Translations.t.general.share}>
 | 
				
			||||||
  <slot name="content">
 | 
					  <slot name="content">
 | 
				
			||||||
    <Share class="h-7 w-7 p-1" />
 | 
					    <Share class="h-7 w-7 p-1" />
 | 
				
			||||||
  </slot>
 | 
					  </slot>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
  import Tr from "../Base/Tr.svelte"
 | 
					  import Tr from "../Base/Tr.svelte"
 | 
				
			||||||
  import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"
 | 
					  import Mapillary_black from "../../assets/svg/Mapillary_black.svelte"
 | 
				
			||||||
  import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
 | 
					  import { Mapillary } from "../../Logic/ImageProviders/Mapillary"
 | 
				
			||||||
 | 
					  import { twMerge } from "tailwind-merge"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
      A subtleButton which opens mapillary in a new tab at the current location
 | 
					      A subtleButton which opens mapillary in a new tab at the current location
 | 
				
			||||||
| 
						 | 
					@ -16,12 +17,17 @@
 | 
				
			||||||
  let location = mapProperties.location
 | 
					  let location = mapProperties.location
 | 
				
			||||||
  let zoom = mapProperties.zoom
 | 
					  let zoom = mapProperties.zoom
 | 
				
			||||||
  let mapillaryLink = Mapillary.createLink($location, $zoom)
 | 
					  let mapillaryLink = Mapillary.createLink($location, $zoom)
 | 
				
			||||||
 | 
					  export let large: boolean = true
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<a class="button flex items-center" href={mapillaryLink} target="_blank">
 | 
					<a class="flex items-center" href={mapillaryLink} target="_blank">
 | 
				
			||||||
  <Mapillary_black class="m-2 mr-4 h-12 w-12 shrink-0" />
 | 
					  <Mapillary_black class={twMerge("shrink-0",large ? "m-2 mr-4 h-12 w-12" : "w-6 h-6 pr-2")} />
 | 
				
			||||||
 | 
					  {#if large}
 | 
				
			||||||
    <div class="flex flex-col">
 | 
					    <div class="flex flex-col">
 | 
				
			||||||
      <Tr t={Translations.t.general.attribution.openMapillary} />
 | 
					      <Tr t={Translations.t.general.attribution.openMapillary} />
 | 
				
			||||||
      <Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} />
 | 
					      <Tr cls="subtle" t={Translations.t.general.attribution.mapillaryHelp} />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					  {:else}
 | 
				
			||||||
 | 
					    <Tr t={Translations.t.general.attribution.openMapillary} />
 | 
				
			||||||
 | 
					  {/if}
 | 
				
			||||||
</a>
 | 
					</a>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@
 | 
				
			||||||
  }/${$location?.lon ?? 0}`
 | 
					  }/${$location?.lon ?? 0}`
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<a class="button flex items-center" target="_blank" href={idLink}>
 | 
					<a class="flex items-center" target="_blank" href={idLink}>
 | 
				
			||||||
  <PencilIcon class="h-12 w-12 p-2 pr-4" />
 | 
					  <PencilIcon class="h-6 w-6 pr-2" />
 | 
				
			||||||
  <Tr t={Translations.t.general.attribution.editId} />
 | 
					  <Tr t={Translations.t.general.attribution.editId} />
 | 
				
			||||||
</a>
 | 
					</a>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,9 @@
 | 
				
			||||||
import Motion from "../../Sensors/Motion"
 | 
					import Motion from "../../Sensors/Motion"
 | 
				
			||||||
import { Geocoding } from "../../Logic/Osm/Geocoding"
 | 
					import { Geocoding } from "../../Logic/Osm/Geocoding"
 | 
				
			||||||
import type { MapProperties } from "../../Models/MapProperties"
 | 
					import type { MapProperties } from "../../Models/MapProperties"
 | 
				
			||||||
 | 
					import Hotkeys from "../Base/Hotkeys"
 | 
				
			||||||
 | 
					import Translations from "../i18n/Translations"
 | 
				
			||||||
 | 
					import Locale from "../i18n/Locale"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export let mapProperties: MapProperties
 | 
					export let mapProperties: MapProperties
 | 
				
			||||||
let lastDisplayed: Date = undefined
 | 
					let lastDisplayed: Date = undefined
 | 
				
			||||||
| 
						 | 
					@ -14,32 +17,36 @@ async function displayLocation() {
 | 
				
			||||||
  let result = await Geocoding.reverse(
 | 
					  let result = await Geocoding.reverse(
 | 
				
			||||||
    mapProperties.location.data,
 | 
					    mapProperties.location.data,
 | 
				
			||||||
    mapProperties.zoom.data,
 | 
					    mapProperties.zoom.data,
 | 
				
			||||||
 | 
					    Locale.language.data
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  console.log("Got result", result)
 | 
					 | 
				
			||||||
  let properties = result.features[0].properties
 | 
					  let properties = result.features[0].properties
 | 
				
			||||||
  currentLocation = properties.display_name
 | 
					  currentLocation = properties.display_name
 | 
				
			||||||
  window.setTimeout(() => {
 | 
					  window.setTimeout(() => {
 | 
				
			||||||
 | 
					    if(properties.display_name !== currentLocation){
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    currentLocation = undefined
 | 
					    currentLocation = undefined
 | 
				
			||||||
  }, 5000)
 | 
					  }, 5000)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Motion.singleton.lastShakeEvent.addCallbackD(shaken => {
 | 
					Motion.singleton.lastShakeEvent.addCallbackD(shaken => {
 | 
				
			||||||
  console.log("Got a shaken event")
 | 
					  if (lastDisplayed !== undefined && shaken.getTime() - lastDisplayed.getTime() < 2000) {
 | 
				
			||||||
  if (shaken.getTime() - lastDisplayed.getTime() < 1000) {
 | 
					    return
 | 
				
			||||||
    console.log("To soon:",shaken.getTime() - lastDisplayed.getTime())
 | 
					 | 
				
			||||||
   // return
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  displayLocation()
 | 
					  displayLocation()
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					Hotkeys.RegisterHotkey({ nomod: "q" },
 | 
				
			||||||
 | 
					  Translations.t.hotkeyDocumentation.queryCurrentLocation,
 | 
				
			||||||
 | 
					  () => {
 | 
				
			||||||
 | 
					    displayLocation()
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Motion.singleton.startListening()
 | 
					Motion.singleton.startListening()
 | 
				
			||||||
mapProperties.location.stabilized(500).addCallbackAndRun(loc => {
 | 
					 | 
				
			||||||
  displayLocation()
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{#if currentLocation}
 | 
					{#if currentLocation}
 | 
				
			||||||
  <div role="alert" aria-live="assertive" class="normal-background">
 | 
					  <div role="alert" aria-live="assertive" class="normal-background rounded-full border-interactive px-2">
 | 
				
			||||||
    {currentLocation}
 | 
					    {currentLocation}
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
{/if}
 | 
					{/if}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <button on:click={() => state.selectedElement.setData(undefined)}
 | 
					    <button on:click={() => state.selectedElement.setData(undefined)}
 | 
				
			||||||
            use:ariaLabel={Translations.t.general.backToMap}
 | 
					            use:ariaLabel={Translations.t.general.backToMap}
 | 
				
			||||||
            class="border-none p-0">
 | 
					            class="border-none p-0 rounded-full">
 | 
				
			||||||
      <XCircleIcon aria-hidden={true} class="h-8 w-8" />
 | 
					      <XCircleIcon aria-hidden={true} class="h-8 w-8" />
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,10 +7,14 @@
 | 
				
			||||||
  import ThemeViewState from "../../Models/ThemeViewState"
 | 
					  import ThemeViewState from "../../Models/ThemeViewState"
 | 
				
			||||||
  import Summary from "./Summary.svelte"
 | 
					  import Summary from "./Summary.svelte"
 | 
				
			||||||
  import Tr from "../Base/Tr.svelte"
 | 
					  import Tr from "../Base/Tr.svelte"
 | 
				
			||||||
  import { UIEventSource } from "../../Logic/UIEventSource"
 | 
					  import { Store, UIEventSource } from "../../Logic/UIEventSource"
 | 
				
			||||||
  import type { KeyNavigationEvent } from "../../Models/MapProperties"
 | 
					  import type { KeyNavigationEvent } from "../../Models/MapProperties"
 | 
				
			||||||
 | 
					  import type { Feature } from "geojson"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let state: ThemeViewState
 | 
					  export let state: ThemeViewState
 | 
				
			||||||
 | 
					  export let featuresInViewPort: Store<Feature[]>
 | 
				
			||||||
 | 
					  console.log("Visual feedback panel:", featuresInViewPort)
 | 
				
			||||||
 | 
					  const t = Translations.t.general.visualFeedback
 | 
				
			||||||
  let centerFeatures = state.closestFeatures.features
 | 
					  let centerFeatures = state.closestFeatures.features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let lastAction: UIEventSource<KeyNavigationEvent> = new UIEventSource<KeyNavigationEvent>(undefined)
 | 
					  let lastAction: UIEventSource<KeyNavigationEvent> = new UIEventSource<KeyNavigationEvent>(undefined)
 | 
				
			||||||
| 
						 | 
					@ -22,12 +26,12 @@
 | 
				
			||||||
<div aria-live="assertive" class="p-1" role="alert">
 | 
					<div aria-live="assertive" class="p-1" role="alert">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  {#if $lastAction !== undefined}
 | 
					  {#if $lastAction !== undefined}
 | 
				
			||||||
    <Tr t={Translations.t.general.visualFeedback[$lastAction.key]} />
 | 
					    <Tr t={t[$lastAction.key]} />
 | 
				
			||||||
  {:else if $centerFeatures.length === 0}
 | 
					  {:else if $centerFeatures.length === 0}
 | 
				
			||||||
    <Tr t={Translations.t.general.visualFeedback.noCloseFeatures} />
 | 
					    <Tr t={t.noCloseFeatures} />
 | 
				
			||||||
  {:else}
 | 
					  {:else}
 | 
				
			||||||
    <div class="pointer-events-auto">
 | 
					    <div class="pointer-events-auto">
 | 
				
			||||||
      <Tr t={Translations.t.general.visualFeedback.closestFeaturesAre} />
 | 
					      <Tr t={t.closestFeaturesAre.Subs({n: $featuresInViewPort?.length})} />
 | 
				
			||||||
      <ol class="list-none">
 | 
					      <ol class="list-none">
 | 
				
			||||||
        {#each $centerFeatures as feat, i (feat.properties.id)}
 | 
					        {#each $centerFeatures as feat, i (feat.properties.id)}
 | 
				
			||||||
          <li class="flex">
 | 
					          <li class="flex">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,6 @@
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    altmap.data.resize()
 | 
					    altmap.data.resize()
 | 
				
			||||||
    const { lon, lat } = placedOverMapProperties.location.data
 | 
					 | 
				
			||||||
    const altMapCenter = pixelCenterOf(altmap)
 | 
					    const altMapCenter = pixelCenterOf(altmap)
 | 
				
			||||||
    const c = placedOverMap.data.unproject(altMapCenter)
 | 
					    const c = placedOverMap.data.unproject(altMapCenter)
 | 
				
			||||||
    altproperties.location.setData({ lon: c.lng, lat: c.lat })
 | 
					    altproperties.location.setData({ lon: c.lng, lat: c.lat })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -429,7 +429,7 @@ class LineRenderingLayer {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class ShowDataLayer {
 | 
					export default class ShowDataLayer {
 | 
				
			||||||
    private static rangeLayer = new LayerConfig(<any>range_layer, "ShowDataLayer.ts:range.json")
 | 
					    public static rangeLayer = new LayerConfig(<any>range_layer, "ShowDataLayer.ts:range.json")
 | 
				
			||||||
    private readonly _options: ShowDataLayerOptions & {
 | 
					    private readonly _options: ShowDataLayerOptions & {
 | 
				
			||||||
        layer: LayerConfig
 | 
					        layer: LayerConfig
 | 
				
			||||||
        drawMarkers?: true | boolean
 | 
					        drawMarkers?: true | boolean
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -677,6 +677,7 @@ This list will be sorted
 | 
				
			||||||
        /* We calculate the ranges when it is opened! */
 | 
					        /* We calculate the ranges when it is opened! */
 | 
				
			||||||
        return { startingMonday: lastMonday, ranges: OH.GetRanges(oh, lastMonday, nextSunday) }
 | 
					        return { startingMonday: lastMonday, ranges: OH.GetRanges(oh, lastMonday, nextSunday) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) {
 | 
					    public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) {
 | 
				
			||||||
        console.log("Checking identical:", openingRanges)
 | 
					        console.log("Checking identical:", openingRanges)
 | 
				
			||||||
        const monday = openingRanges[startday]
 | 
					        const monday = openingRanges[startday]
 | 
				
			||||||
| 
						 | 
					@ -928,21 +929,6 @@ export class ToTextualDescription {
 | 
				
			||||||
            return t.all_days_from.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
 | 
					            return t.all_days_from.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (OH.weekdaysIdentical(ranges, 0, 4) && OH.weekdaysIdentical(ranges, 5, 6)) {
 | 
					 | 
				
			||||||
            let result = []
 | 
					 | 
				
			||||||
            if (ranges[0].length > 0) {
 | 
					 | 
				
			||||||
                result.push(
 | 
					 | 
				
			||||||
                    t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (ranges[6].length > 0) {
 | 
					 | 
				
			||||||
                result.push(
 | 
					 | 
				
			||||||
                    t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[5]) })
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return ToTextualDescription.chain(result)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const result: Translation[] = []
 | 
					        const result: Translation[] = []
 | 
				
			||||||
        const weekdays = [
 | 
					        const weekdays = [
 | 
				
			||||||
            "monday",
 | 
					            "monday",
 | 
				
			||||||
| 
						 | 
					@ -953,7 +939,9 @@ export class ToTextualDescription {
 | 
				
			||||||
            "saturday",
 | 
					            "saturday",
 | 
				
			||||||
            "sunday",
 | 
					            "sunday",
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
        for (let i = 0; i < weekdays.length; i++) {
 | 
					
 | 
				
			||||||
 | 
					        function addRange(start: number, end: number) {
 | 
				
			||||||
 | 
					            for (let i = start; i <= end; i++) {
 | 
				
			||||||
                const day = weekdays[i]
 | 
					                const day = weekdays[i]
 | 
				
			||||||
                if (ranges[i]?.length > 0) {
 | 
					                if (ranges[i]?.length > 0) {
 | 
				
			||||||
                    result.push(
 | 
					                    result.push(
 | 
				
			||||||
| 
						 | 
					@ -961,6 +949,23 @@ export class ToTextualDescription {
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (OH.weekdaysIdentical(ranges, 0, 4)) {
 | 
				
			||||||
 | 
					            result.push(
 | 
				
			||||||
 | 
					                t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[0]) })
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            addRange(0, 4)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (OH.weekdaysIdentical(ranges, 5, 6)) {
 | 
				
			||||||
 | 
					            result.push(
 | 
				
			||||||
 | 
					                t.on_weekdays.Subs({ ranges: ToTextualDescription.createRangesFor(ranges[5]) })
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            addRange(5, 6)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        return ToTextualDescription.chain(result)
 | 
					        return ToTextualDescription.chain(result)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -972,6 +977,7 @@ export class ToTextualDescription {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return tr
 | 
					        return tr
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static timeString(date: Date) {
 | 
					    private static timeString(date: Date) {
 | 
				
			||||||
        return OH.hhmm(date.getHours(), date.getMinutes())
 | 
					        return OH.hhmm(date.getHours(), date.getMinutes())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@
 | 
				
			||||||
      <HeartSolidIcon aria-hidden={true} />
 | 
					      <HeartSolidIcon aria-hidden={true} />
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
  {:else}
 | 
					  {:else}
 | 
				
			||||||
    <button class="p-0 m-0 h-8 w-8 no-image-background" on:click={() => markFavourite(true)}
 | 
					    <button class="p-0 m-0 h-8 w-8 no-image-background soft" on:click={() => markFavourite(true)}
 | 
				
			||||||
            use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}>
 | 
					            use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}>
 | 
				
			||||||
      <HeartOutlineIcon aria-hidden={true} />
 | 
					      <HeartOutlineIcon aria-hidden={true} />
 | 
				
			||||||
    </button>
 | 
					    </button>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,6 @@
 | 
				
			||||||
  import Tr from "./Base/Tr.svelte"
 | 
					  import Tr from "./Base/Tr.svelte"
 | 
				
			||||||
  import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
 | 
					  import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
 | 
				
			||||||
  import FloatOver from "./Base/FloatOver.svelte"
 | 
					  import FloatOver from "./Base/FloatOver.svelte"
 | 
				
			||||||
  import PrivacyPolicy from "./BigComponents/PrivacyPolicy"
 | 
					 | 
				
			||||||
  import Constants from "../Models/Constants"
 | 
					  import Constants from "../Models/Constants"
 | 
				
			||||||
  import TabbedGroup from "./Base/TabbedGroup.svelte"
 | 
					  import TabbedGroup from "./Base/TabbedGroup.svelte"
 | 
				
			||||||
  import UserRelatedState from "../Logic/State/UserRelatedState"
 | 
					  import UserRelatedState from "../Logic/State/UserRelatedState"
 | 
				
			||||||
| 
						 | 
					@ -54,7 +53,6 @@
 | 
				
			||||||
  import Plus from "../assets/svg/Plus.svelte"
 | 
					  import Plus from "../assets/svg/Plus.svelte"
 | 
				
			||||||
  import Filter from "../assets/svg/Filter.svelte"
 | 
					  import Filter from "../assets/svg/Filter.svelte"
 | 
				
			||||||
  import Add from "../assets/svg/Add.svelte"
 | 
					  import Add from "../assets/svg/Add.svelte"
 | 
				
			||||||
  import Statistics from "../assets/svg/Statistics.svelte"
 | 
					 | 
				
			||||||
  import Community from "../assets/svg/Community.svelte"
 | 
					  import Community from "../assets/svg/Community.svelte"
 | 
				
			||||||
  import Download from "../assets/svg/Download.svelte"
 | 
					  import Download from "../assets/svg/Download.svelte"
 | 
				
			||||||
  import Share from "../assets/svg/Share.svelte"
 | 
					  import Share from "../assets/svg/Share.svelte"
 | 
				
			||||||
| 
						 | 
					@ -66,6 +64,11 @@
 | 
				
			||||||
  import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
 | 
					  import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
 | 
				
			||||||
  import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
 | 
					  import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
 | 
				
			||||||
  import FilterPanel from "./BigComponents/FilterPanel.svelte"
 | 
					  import FilterPanel from "./BigComponents/FilterPanel.svelte"
 | 
				
			||||||
 | 
					  import PrivacyPolicy from "./BigComponents/PrivacyPolicy.svelte"
 | 
				
			||||||
 | 
					  import { BBox } from "../Logic/BBox"
 | 
				
			||||||
 | 
					  import { GeoOperations } from "../Logic/GeoOperations"
 | 
				
			||||||
 | 
					  import ShowDataLayer from "./Map/ShowDataLayer"
 | 
				
			||||||
 | 
					  import StaticFeatureSource from "../Logic/FeatureSource/Sources/StaticFeatureSource"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  export let state: ThemeViewState
 | 
					  export let state: ThemeViewState
 | 
				
			||||||
  let layout = state.layout
 | 
					  let layout = state.layout
 | 
				
			||||||
| 
						 | 
					@ -100,8 +103,30 @@
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  let currentZoom = state.mapProperties.zoom
 | 
					  let currentZoom = state.mapProperties.zoom
 | 
				
			||||||
  let showCrosshair = state.userRelatedState.showCrosshair
 | 
					  let showCrosshair = state.userRelatedState.showCrosshair
 | 
				
			||||||
  let arrowKeysWereUsed = state.visualFeedback
 | 
					  let visualFeedback = state.visualFeedback
 | 
				
			||||||
  let centerFeatures = state.closestFeatures.features
 | 
					  let viewport: UIEventSource<HTMLDivElement> = new UIEventSource<HTMLDivElement>(undefined)
 | 
				
			||||||
 | 
					  let featuresInViewPort: UIEventSource<Feature[]> = new UIEventSource<Feature[]>(undefined)
 | 
				
			||||||
 | 
					  viewport.addCallbackAndRunD(viewport => {
 | 
				
			||||||
 | 
					    state.featuresInView.features.addCallbackAndRunD((features: Feature[]) => {
 | 
				
			||||||
 | 
					      const rect = viewport.getBoundingClientRect()
 | 
				
			||||||
 | 
					      const mlmap = state.map.data
 | 
				
			||||||
 | 
					      if (!mlmap) {
 | 
				
			||||||
 | 
					        return undefined
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      const topLeft = mlmap.unproject([rect.left, rect.top])
 | 
				
			||||||
 | 
					      const bottomRight = mlmap.unproject([rect.right, rect.bottom])
 | 
				
			||||||
 | 
					      const bbox = new BBox([[topLeft.lng, topLeft.lat], [bottomRight.lng, bottomRight.lat]])
 | 
				
			||||||
 | 
					      const bboxGeo = bbox.asGeoJson({})
 | 
				
			||||||
 | 
					      console.log("BBOX:", bboxGeo)
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      const filtered = features.filter((f: Feature) => {
 | 
				
			||||||
 | 
					        console.log(f, bboxGeo)
 | 
				
			||||||
 | 
					        return GeoOperations.calculateOverlap(bboxGeo, [f]).length > 0
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      featuresInViewPort.setData(filtered)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
  let mapproperties: MapProperties = state.mapProperties
 | 
					  let mapproperties: MapProperties = state.mapProperties
 | 
				
			||||||
  let featureSwitches: FeatureSwitchState = state.featureSwitches
 | 
					  let featureSwitches: FeatureSwitchState = state.featureSwitches
 | 
				
			||||||
  let availableLayers = state.availableLayers
 | 
					  let availableLayers = state.availableLayers
 | 
				
			||||||
| 
						 | 
					@ -133,6 +158,13 @@
 | 
				
			||||||
  <MaplibreMap map={maplibremap} />
 | 
					  <MaplibreMap map={maplibremap} />
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{#if $visualFeedback}
 | 
				
			||||||
 | 
					  <div class="absolute top-0 left-0 h-screen w-screen overflow-hidden flex items-center justify-center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div bind:this={$viewport} style="border: 2px solid #ff000044; width: 300px; height: 300px"></div>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					{/if}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div class="pointer-events-none absolute top-0 left-0 w-full">
 | 
					<div class="pointer-events-none absolute top-0 left-0 w-full">
 | 
				
			||||||
  <!-- Top components -->
 | 
					  <!-- Top components -->
 | 
				
			||||||
  <If condition={state.featureSwitches.featureSwitchSearch}>
 | 
					  <If condition={state.featureSwitches.featureSwitchSearch}>
 | 
				
			||||||
| 
						 | 
					@ -183,7 +215,7 @@
 | 
				
			||||||
      <div class="alert w-fit">Testmode</div>
 | 
					      <div class="alert w-fit">Testmode</div>
 | 
				
			||||||
    </If>
 | 
					    </If>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
  <div class="flex w-full justify-center">
 | 
					  <div class="flex flex-col w-full justify-center items-center">
 | 
				
			||||||
    <!-- Flex and w-full are needed for the positioning -->
 | 
					    <!-- Flex and w-full are needed for the positioning -->
 | 
				
			||||||
    <!-- Centermessage -->
 | 
					    <!-- Centermessage -->
 | 
				
			||||||
    <StateIndicator {state} />
 | 
					    <StateIndicator {state} />
 | 
				
			||||||
| 
						 | 
					@ -238,7 +270,7 @@
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <If condition={state.visualFeedback}>
 | 
					    <If condition={state.visualFeedback}>
 | 
				
			||||||
      <VisualFeedbackPanel {state} />
 | 
					      <VisualFeedbackPanel {state} {featuresInViewPort} />
 | 
				
			||||||
    </If>
 | 
					    </If>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <div class="flex flex-col items-end">
 | 
					    <div class="flex flex-col items-end">
 | 
				
			||||||
| 
						 | 
					@ -265,7 +297,7 @@
 | 
				
			||||||
        <Min class="h-8 w-8" />
 | 
					        <Min class="h-8 w-8" />
 | 
				
			||||||
      </MapControlButton>
 | 
					      </MapControlButton>
 | 
				
			||||||
      <If condition={featureSwitches.featureSwitchGeolocation}>
 | 
					      <If condition={featureSwitches.featureSwitchGeolocation}>
 | 
				
			||||||
        <div class="relative m-0.5 md:m-1">
 | 
					        <div class="relative m-0">
 | 
				
			||||||
          <MapControlButton arialabel={Translations.t.general.labels.jumpToLocation}
 | 
					          <MapControlButton arialabel={Translations.t.general.labels.jumpToLocation}
 | 
				
			||||||
                            on:click={() => state.geolocationControl.handleClick()}
 | 
					                            on:click={() => state.geolocationControl.handleClick()}
 | 
				
			||||||
                            on:keydown={forwardEventToMap}
 | 
					                            on:keydown={forwardEventToMap}
 | 
				
			||||||
| 
						 | 
					@ -288,7 +320,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<LoginToggle ignoreLoading={true} {state}>
 | 
					<LoginToggle ignoreLoading={true} {state}>
 | 
				
			||||||
  {#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $arrowKeysWereUsed}
 | 
					  {#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $visualFeedback}
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
      class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center justify-center"
 | 
					      class="pointer-events-none absolute top-0 left-0 flex h-full w-full items-center justify-center"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
| 
						 | 
					@ -416,6 +448,7 @@
 | 
				
			||||||
  </FloatOver>
 | 
					  </FloatOver>
 | 
				
			||||||
</IfHidden>
 | 
					</IfHidden>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<If condition={state.guistate.menuIsOpened}>
 | 
					<If condition={state.guistate.menuIsOpened}>
 | 
				
			||||||
  <!-- Menu page -->
 | 
					  <!-- Menu page -->
 | 
				
			||||||
  <FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
 | 
					  <FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
 | 
				
			||||||
| 
						 | 
					@ -458,10 +491,27 @@
 | 
				
			||||||
          <Tr t={Translations.t.general.attribution.donate} />
 | 
					          <Tr t={Translations.t.general.attribution.donate} />
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <a class="flex" href={Utils.OsmChaLinkFor(7)} target="_blank">
 | 
					        <button class="small soft flex" on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}>
 | 
				
			||||||
          <Statistics class="h-6 w-6" />
 | 
					          <Community class="h-6 w-6" />
 | 
				
			||||||
          <Tr t={Translations.t.general.attribution.openOsmcha.Subs({ theme: "MapComplete" })} />
 | 
					          <Tr t={Translations.t.communityIndex.title} />
 | 
				
			||||||
        </a>
 | 
					        </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <If condition={featureSwitches.featureSwitchEnableLogin}>
 | 
				
			||||||
 | 
					          <OpenIdEditor mapProperties={state.mapProperties} />
 | 
				
			||||||
 | 
					          <OpenJosm {state} />
 | 
				
			||||||
 | 
					          <MapillaryLink large={false} mapProperties={state.mapProperties} />
 | 
				
			||||||
 | 
					        </If>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <button class="small soft flex"
 | 
				
			||||||
 | 
					                on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          <EyeIcon class="w-6 h-6 pr-1" />
 | 
				
			||||||
 | 
					          <Tr t={Translations.t.privacy.title} />
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					        <div class="m-2 flex flex-col">
 | 
				
			||||||
 | 
					          <ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
        {Constants.vNumber}
 | 
					        {Constants.vNumber}
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -503,31 +553,40 @@
 | 
				
			||||||
        </h3>
 | 
					        </h3>
 | 
				
			||||||
        <Favourites {state} />
 | 
					        <Favourites {state} />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
      <div class="flex" slot="title3">
 | 
					 | 
				
			||||||
        <Community class="h-6 w-6" />
 | 
					 | 
				
			||||||
        <Tr t={Translations.t.communityIndex.title} />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="m-2" slot="content3">
 | 
					 | 
				
			||||||
        <CommunityIndexView location={state.mapProperties.location} />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="flex" slot="title4">
 | 
					 | 
				
			||||||
        <EyeIcon class="w-6" />
 | 
					 | 
				
			||||||
        <Tr t={Translations.t.privacy.title} />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
      <div class="m-2" slot="content4">
 | 
					 | 
				
			||||||
        <ToSvelte construct={() => new PrivacyPolicy()} />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <Tr slot="title5" t={Translations.t.advanced.title} />
 | 
					 | 
				
			||||||
      <div class="m-2 flex flex-col" slot="content5">
 | 
					 | 
				
			||||||
        <If condition={featureSwitches.featureSwitchEnableLogin}>
 | 
					 | 
				
			||||||
          <OpenIdEditor mapProperties={state.mapProperties} />
 | 
					 | 
				
			||||||
          <OpenJosm {state} />
 | 
					 | 
				
			||||||
          <MapillaryLink mapProperties={state.mapProperties} />
 | 
					 | 
				
			||||||
        </If>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <ToSvelte construct={Hotkeys.generateDocumentationDynamic} />
 | 
					 | 
				
			||||||
      </div>
 | 
					 | 
				
			||||||
    </TabbedGroup>
 | 
					    </TabbedGroup>
 | 
				
			||||||
  </FloatOver>
 | 
					  </FloatOver>
 | 
				
			||||||
</If>
 | 
					</If>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<If condition={state.guistate.privacyPanelIsOpened}>
 | 
				
			||||||
 | 
					  <FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
 | 
				
			||||||
 | 
					    <div class="h-full flex flex-col overflow-hidden">
 | 
				
			||||||
 | 
					      <h2 class="flex items-center low-interaction p-4 m-0">
 | 
				
			||||||
 | 
					        <EyeIcon class="w-6 pr-2" />
 | 
				
			||||||
 | 
					        <Tr t={Translations.t.privacy.title} />
 | 
				
			||||||
 | 
					      </h2>
 | 
				
			||||||
 | 
					      <div class="overflow-auto p-4">
 | 
				
			||||||
 | 
					        <PrivacyPolicy />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </FloatOver>
 | 
				
			||||||
 | 
					</If>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<If condition={state.guistate.communityIndexPanelIsOpened}>
 | 
				
			||||||
 | 
					  <FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
 | 
				
			||||||
 | 
					    <div class="h-full flex flex-col overflow-hidden">
 | 
				
			||||||
 | 
					      <h2 class="flex items-center low-interaction p-4 m-0">
 | 
				
			||||||
 | 
					        <Community class="h-6 w-6" />
 | 
				
			||||||
 | 
					        <Tr t={Translations.t.communityIndex.title} />
 | 
				
			||||||
 | 
					      </h2>
 | 
				
			||||||
 | 
					      <div class="overflow-auto p-4">
 | 
				
			||||||
 | 
					        <CommunityIndexView location={state.mapProperties.location} />
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  </FloatOver>
 | 
				
			||||||
 | 
					</If>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -350,6 +350,11 @@ label.checked:not(.neutral-label) {
 | 
				
			||||||
    width: 100%;
 | 
					    width: 100%;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button.soft, .button.soft {
 | 
				
			||||||
 | 
					    border: 2px solid var(--interactive-background);
 | 
				
			||||||
 | 
					    margin: 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.links-as-button a {
 | 
					.links-as-button a {
 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
    * Let a 'link' mimick a button, but not entirely
 | 
					    * Let a 'link' mimick a button, but not entirely
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue