forked from MapComplete/MapComplete
		
	Merge develop
This commit is contained in:
		
							parent
							
								
									c62705c1dd
								
							
						
					
					
						commit
						c167094b65
					
				
					 12 changed files with 251 additions and 139 deletions
				
			
		|  | @ -1465,13 +1465,51 @@ | ||||||
|       "#force-save-button": "yes" |       "#force-save-button": "yes" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "id": "debug-gps", |       "id": "debug-gps-group", | ||||||
|       "condition": "mapcomplete-show_debug=yes", |       "condition": "mapcomplete-show_debug=yes", | ||||||
|  |       "render": { | ||||||
|  |         "special": { | ||||||
|  |           "type": "group", | ||||||
|  |           "header": "debug-gps-title", | ||||||
|  |           "labels": "debug-gps" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "id": "debug-gps-title", | ||||||
|  |       "labels": ["hidden"], | ||||||
|  |       "render": { | ||||||
|  |         "en": "GPS info" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "id": "debug-gps", | ||||||
|  |       "labels": ["hidden"], | ||||||
|       "render": "{gps_all_tags()}" |       "render": "{gps_all_tags()}" | ||||||
|     }, |     }, | ||||||
|  | 
 | ||||||
|  |     { | ||||||
|  |       "id": "debug-info-group", | ||||||
|  |       "condition": "mapcomplete-show_debug=yes", | ||||||
|  | 
 | ||||||
|  |       "render": { | ||||||
|  |         "special": { | ||||||
|  |           "type": "group", | ||||||
|  |           "header": "debug-tags-title", | ||||||
|  |           "labels": "debug" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "id": "debug-tags-title", | ||||||
|  |       "labels": ["hidden"], | ||||||
|  |       "render": { | ||||||
|  |         "en": "Debug info" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     { |     { | ||||||
|       "id": "debug", |       "id": "debug", | ||||||
|       "condition": "mapcomplete-show_debug=yes", |       "labels": ["hidden"], | ||||||
|       "render": "{all_tags()}" |       "render": "{all_tags()}" | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ export interface GeoLocationPointProperties extends GeolocationCoordinates { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * An abstract representation of the current state of the geolocation. |  * An abstract representation of the current state of the geolocation, keeping track of permissions and if a location is known. | ||||||
|  */ |  */ | ||||||
| export class GeoLocationState { | export class GeoLocationState { | ||||||
|     /** |     /** | ||||||
|  | @ -167,8 +167,16 @@ export class GeoLocationState { | ||||||
| 
 | 
 | ||||||
|         if(AndroidPolyfill.inAndroid.data){ |         if(AndroidPolyfill.inAndroid.data){ | ||||||
|             this.permission.setData("requested") |             this.permission.setData("requested") | ||||||
|             AndroidPolyfill.geolocationPermission.addCallbackAndRunD(state => this.permission.set(state)) |             this.permission.addCallbackAndRunD(p => { | ||||||
|             this.startWatching() |                 if(p === "granted"){ | ||||||
|  |                     this.startWatching() | ||||||
|  |                     return true | ||||||
|  |                 } | ||||||
|  |             }) | ||||||
|  |             AndroidPolyfill.requestGeoPermission().then(state => { | ||||||
|  |                 const granted = state.value === "true" | ||||||
|  |                 this.permission.set(granted ? "granted" : "denied") | ||||||
|  |             }) | ||||||
|             return |             return | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -210,6 +218,13 @@ export class GeoLocationState { | ||||||
|      * @private |      * @private | ||||||
|      */ |      */ | ||||||
|     private async startWatching() { |     private async startWatching() { | ||||||
|  | 
 | ||||||
|  |         if(AndroidPolyfill.inAndroid.data){ | ||||||
|  |             AndroidPolyfill.watchLocation( this.currentGPSLocation, location => { | ||||||
|  |                 console.log(JSON.stringify(location)) | ||||||
|  |             }) | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         navigator.geolocation.watchPosition( |         navigator.geolocation.watchPosition( | ||||||
|              (position: GeolocationPosition) => { |              (position: GeolocationPosition) => { | ||||||
|                 this._gpsAvailable.set(true) |                 this._gpsAvailable.set(true) | ||||||
|  |  | ||||||
|  | @ -416,6 +416,10 @@ export default class UserRelatedState { | ||||||
|                 typeof window === "undefined" ? "no" : window.navigator.share ? "yes" : "no", |                 typeof window === "undefined" ? "no" : window.navigator.share ? "yes" : "no", | ||||||
|             _iframe: Utils.isIframe ? "yes" : "no", |             _iframe: Utils.isIframe ? "yes" : "no", | ||||||
|         }) |         }) | ||||||
|  |         if(!Utils.runningFromConsole){ | ||||||
|  |             amendedPrefs.data["_host"] = window.location.host | ||||||
|  |             amendedPrefs.data["_path"] = window.location.pathname | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         for (const key in Constants.userJourney) { |         for (const key in Constants.userJourney) { | ||||||
|             amendedPrefs.data["__userjourney_" + key] = Constants.userJourney[key] |             amendedPrefs.data["__userjourney_" + key] = Constants.userJourney[key] | ||||||
|  |  | ||||||
|  | @ -19,9 +19,9 @@ const DatabridgePluginSingleton = registerPlugin<DatabridgePlugin>("Databridge", | ||||||
|                     return { value: "web" } |                     return { value: "web" } | ||||||
|                 } |                 } | ||||||
|                 return null |                 return null | ||||||
|             } |             }, | ||||||
|         } |         } | ||||||
|     } |     }, | ||||||
| }) | }) | ||||||
| 
 | 
 | ||||||
| export class AndroidPolyfill { | export class AndroidPolyfill { | ||||||
|  | @ -36,12 +36,18 @@ export class AndroidPolyfill { | ||||||
|      * @private |      * @private | ||||||
|      */ |      */ | ||||||
|     private backfillGeolocation(databridgePlugin: DatabridgePlugin) { |     private backfillGeolocation(databridgePlugin: DatabridgePlugin) { | ||||||
|         const src = UIEventSource.FromPromise(databridgePlugin.request({ key: "location:request-permission" })) |         const src = UIEventSource.FromPromise(databridgePlugin.request({ key: "location:has-permission" })) | ||||||
|         src.addCallbackAndRunD(permission => { |         src.addCallbackAndRunD(permission => { | ||||||
|             AndroidPolyfill._geolocationPermission.set(<"granted" | "denied">permission.value) |             console.log("> Checking geopermission gave: ", JSON.stringify(permission), permission.value) | ||||||
|  |             const granted = permission.value === "true" | ||||||
|  |             AndroidPolyfill._geolocationPermission.set(granted ? "granted" : "denied") | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public static async requestGeoPermission(): Promise<{ value: string | object }> { | ||||||
|  |         return DatabridgePluginSingleton.request({ key: "location:request-permission" }) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     public async init() { |     public async init() { | ||||||
|         console.log("Sniffing shell version") |         console.log("Sniffing shell version") | ||||||
|         const shell = await this.databridgePlugin.request({ key: "meta" }) |         const shell = await this.databridgePlugin.request({ key: "meta" }) | ||||||
|  | @ -55,9 +61,9 @@ export class AndroidPolyfill { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static async requestLoginCodes() { |     public static async requestLoginCodes() { | ||||||
|         const result = await DatabridgePluginSingleton.request<{oauth_token: string}>({ key: "request:login" }) |         const result = await DatabridgePluginSingleton.request<{ oauth_token: string }>({ key: "request:login" }) | ||||||
|         const token: string = result.value.oauth_token |         const token: string = result.value.oauth_token | ||||||
|         console.log("AndroidPolyfill: received oauth_token; trying to pass them to the oauth lib",token) |         console.log("AndroidPolyfill: received oauth_token; trying to pass them to the oauth lib", token) | ||||||
|         return token |         return token | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -67,7 +73,7 @@ export class AndroidPolyfill { | ||||||
|         console.log("Registering back button callback", callback) |         console.log("Registering back button callback", callback) | ||||||
|         DatabridgePluginSingleton.request({ key: "backbutton" }).then(ev => { |         DatabridgePluginSingleton.request({ key: "backbutton" }).then(ev => { | ||||||
|             console.log("AndroidPolyfill: received backbutton: ", ev) |             console.log("AndroidPolyfill: received backbutton: ", ev) | ||||||
|             if(ev === null){ |             if (ev === null) { | ||||||
|                 // Probably in web environment
 |                 // Probably in web environment
 | ||||||
|                 return |                 return | ||||||
|             } |             } | ||||||
|  | @ -84,5 +90,26 @@ export class AndroidPolyfill { | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     public static watchLocation(writeInto: UIEventSource<GeolocationCoordinates>, callback: (location) => void) { | ||||||
|  |         DatabridgePluginSingleton.request({ | ||||||
|  |             key: "location:watch", | ||||||
|  |         }).then((l: { | ||||||
|  |             value: { latitude: number, longitude: number, accuraccy: number, altidude: number, heading: number, speed:number } | ||||||
|  |         }) => { | ||||||
|  |             // example l: {"value":{"latitude":51.0618627,"longitude":3.730468566666667,"accuracy":2.0393495559692383,"altitude":46.408,"heading":168.2969970703125}}
 | ||||||
|  |             console.log("Received location from Android:", JSON.stringify(l)) | ||||||
|  |             const loc = l.value | ||||||
|  |             writeInto.set({ | ||||||
|  |                 latitude: loc.latitude, | ||||||
|  |                 longitude: loc.longitude, | ||||||
|  |                 heading: loc.heading, | ||||||
|  |                 accuracy: loc.accuraccy, | ||||||
|  |                 altitude: loc.altidude, | ||||||
|  |                 altitudeAccuracy: undefined, | ||||||
|  |                 speed: loc.speed, | ||||||
|  |             }) | ||||||
|  |         }) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -126,6 +126,9 @@ export default class TagRenderingConfig { | ||||||
|                 this.id |                 this.id | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|  |         if(json.labels && !Array.isArray( json.labels)){ | ||||||
|  |             throw (`Invalid labels at ${context}: labels should be a list of strings, but got a ${typeof json.labels}`) | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         this.labels = json.labels ?? [] |         this.labels = json.labels ?? [] | ||||||
|         if (typeof json.classes === "string") { |         if (typeof json.classes === "string") { | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ | ||||||
|   import { ariaLabel } from "../../Utils/ariaLabel" |   import { ariaLabel } from "../../Utils/ariaLabel" | ||||||
|   import { Translation } from "../i18n/Translation" |   import { Translation } from "../i18n/Translation" | ||||||
|   import Backspace from "@babeard/svelte-heroicons/outline/Backspace" |   import Backspace from "@babeard/svelte-heroicons/outline/Backspace" | ||||||
|  |   import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill" | ||||||
| 
 | 
 | ||||||
|   export let value: UIEventSource<string> |   export let value: UIEventSource<string> | ||||||
|   let _value = value.data ?? "" |   let _value = value.data ?? "" | ||||||
|  | @ -36,6 +37,7 @@ | ||||||
|   if (autofocus) { |   if (autofocus) { | ||||||
|     isFocused.set(true) |     isFocused.set(true) | ||||||
|   } |   } | ||||||
|  |   let isAndroid = AndroidPolyfill.inAndroid | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <form class="w-full" on:submit|preventDefault={() => dispatch("search")}> | <form class="w-full" on:submit|preventDefault={() => dispatch("search")}> | ||||||
|  | @ -62,18 +64,20 @@ | ||||||
|       use:set_placeholder={placeholder} |       use:set_placeholder={placeholder} | ||||||
|       use:ariaLabel={placeholder} |       use:ariaLabel={placeholder} | ||||||
|     /> |     /> | ||||||
| 
 |     {#if !isAndroid} | ||||||
|     {#if $value.length > 0} |       <!-- Show a 'clear field' icon in the searchbar. The android-build already provides this for us, hence the outer if --> | ||||||
|       <Backspace |       {#if $value.length > 0} | ||||||
|         on:click={(e) => { |         <Backspace | ||||||
|  |           on:click={(e) => { | ||||||
|           value.set("") |           value.set("") | ||||||
|           e.preventDefault() |           e.preventDefault() | ||||||
|         }} |         }} | ||||||
|         color="var(--button-background)" |           color="var(--button-background)" | ||||||
|         class="mr-3 h-6 w-6 cursor-pointer" |           class="mr-3 h-6 w-6 cursor-pointer" | ||||||
|       /> |         /> | ||||||
|     {:else} |       {:else} | ||||||
|       <div class="mr-3 w-6" /> |         <div class="mr-3 w-6" /> | ||||||
|  |       {/if} | ||||||
|     {/if} |     {/if} | ||||||
|   </label> |   </label> | ||||||
| </form> | </form> | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
|   import Location_locked from "../../assets/svg/Location_locked.svelte" |   import Location_locked from "../../assets/svg/Location_locked.svelte" | ||||||
|   import Location_unlocked from "../../assets/svg/Location_unlocked.svelte" |   import Location_unlocked from "../../assets/svg/Location_unlocked.svelte" | ||||||
|   import Location from "../../assets/svg/Location.svelte" |   import Location from "../../assets/svg/Location.svelte" | ||||||
|  |   import Location_empty from "../../assets/svg/Location_empty.svelte" | ||||||
| 
 | 
 | ||||||
|   export let state: ThemeViewState |   export let state: ThemeViewState | ||||||
|   let geolocationstate = state.geolocation.geolocationState |   let geolocationstate = state.geolocation.geolocationState | ||||||
|  | @ -31,10 +32,10 @@ | ||||||
| {:else if $geopermission === "denied" || !$isAvailable} | {:else if $geopermission === "denied" || !$isAvailable} | ||||||
|   <Location_refused class={clss} /> |   <Location_refused class={clss} /> | ||||||
| {:else if $geopermission === "prompt"} | {:else if $geopermission === "prompt"} | ||||||
|   <Location class={clss} /> |   <Location_empty class={clss} /> | ||||||
| {:else if $geopermission === "requested"} | {:else if $geopermission === "requested"} | ||||||
|   <!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup --> |   <!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup --> | ||||||
|   <Location class={clss} style="animation: 3s linear 0s infinite normal none running spin;" /> |   <Location_empty class={clss} style="animation: 3s linear 0s infinite normal none running spin;" /> | ||||||
| {:else} | {:else} | ||||||
|   <Location class={clss} style="animation: 3s linear 0s infinite normal none running spin;" /> |   <Location class={clss} style="animation: 3s linear 0s infinite normal none running spin;" /> | ||||||
| {/if} | {/if} | ||||||
|  |  | ||||||
|  | @ -319,7 +319,7 @@ | ||||||
|   if (state?.osmConnection) { |   if (state?.osmConnection) { | ||||||
|     onDestroy( |     onDestroy( | ||||||
|       state.osmConnection?.userDetails?.addCallbackAndRun((ud) => { |       state.osmConnection?.userDetails?.addCallbackAndRun((ud) => { | ||||||
|         numberOfCs = ud.csCount |         numberOfCs = ud?.csCount | ||||||
|       }) |       }) | ||||||
|     ) |     ) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -122,7 +122,7 @@ class NearbyImageVis implements SpecialVisualization { | ||||||
|         tags: UIEventSource<Record<string, string>>, |         tags: UIEventSource<Record<string, string>>, | ||||||
|         args: string[], |         args: string[], | ||||||
|         feature: Feature, |         feature: Feature, | ||||||
|         layer: LayerConfig |         layer: LayerConfig, | ||||||
|     ): SvelteUIElement { |     ): SvelteUIElement { | ||||||
|         const isOpen = args[0] === "open" |         const isOpen = args[0] === "open" | ||||||
|         const readonly = args[1] === "readonly" || args[1] === "yes" |         const readonly = args[1] === "readonly" || args[1] === "yes" | ||||||
|  | @ -189,7 +189,7 @@ class StealViz implements SpecialVisualization { | ||||||
|                                 selectedElement: otherFeature, |                                 selectedElement: otherFeature, | ||||||
|                                 state, |                                 state, | ||||||
|                                 layer, |                                 layer, | ||||||
|                             }) |                             }), | ||||||
|                         ) |                         ) | ||||||
|                     } |                     } | ||||||
|                     if (elements.length === 1) { |                     if (elements.length === 1) { | ||||||
|  | @ -197,8 +197,8 @@ class StealViz implements SpecialVisualization { | ||||||
|                     } |                     } | ||||||
|                     return new Combine(elements).SetClass("flex flex-col") |                     return new Combine(elements).SetClass("flex flex-col") | ||||||
|                 }, |                 }, | ||||||
|                 [state.indexedFeatures.featuresById] |                 [state.indexedFeatures.featuresById], | ||||||
|             ) |             ), | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -250,11 +250,11 @@ class CloseNoteViz implements SpecialVisualization { | ||||||
|     public constr( |     public constr( | ||||||
|         state: SpecialVisualizationState, |         state: SpecialVisualizationState, | ||||||
|         tags: UIEventSource<Record<string, string>>, |         tags: UIEventSource<Record<string, string>>, | ||||||
|         args: string[] |         args: string[], | ||||||
|     ): SvelteUIElement { |     ): SvelteUIElement { | ||||||
|         const { text, icon, idkey, comment, minZoom, zoomButton } = Utils.ParseVisArgs( |         const { text, icon, idkey, comment, minZoom, zoomButton } = Utils.ParseVisArgs( | ||||||
|             this.args, |             this.args, | ||||||
|             args |             args, | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         return new SvelteUIElement(CloseNoteButton, { |         return new SvelteUIElement(CloseNoteButton, { | ||||||
|  | @ -295,7 +295,7 @@ export class QuestionViz implements SpecialVisualization { | ||||||
|         tags: UIEventSource<Record<string, string>>, |         tags: UIEventSource<Record<string, string>>, | ||||||
|         args: string[], |         args: string[], | ||||||
|         feature: Feature, |         feature: Feature, | ||||||
|         layer: LayerConfig |         layer: LayerConfig, | ||||||
|     ): SvelteUIElement { |     ): SvelteUIElement { | ||||||
|         const labels = args[0] |         const labels = args[0] | ||||||
|             ?.split(";") |             ?.split(";") | ||||||
|  | @ -327,7 +327,7 @@ export default class SpecialVisualizations { | ||||||
|         for (const specialVisualization of SpecialVisualizations.specialVisualizations) { |         for (const specialVisualization of SpecialVisualizations.specialVisualizations) { | ||||||
|             SpecialVisualizations.specialVisualisationsDict.set( |             SpecialVisualizations.specialVisualisationsDict.set( | ||||||
|                 specialVisualization.funcName, |                 specialVisualization.funcName, | ||||||
|                 specialVisualization |                 specialVisualization, | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -347,15 +347,15 @@ export default class SpecialVisualizations { | ||||||
|             viz.docs, |             viz.docs, | ||||||
|             viz.args.length > 0 |             viz.args.length > 0 | ||||||
|                 ? MarkdownUtils.table( |                 ? MarkdownUtils.table( | ||||||
|                       ["name", "default", "description"], |                     ["name", "default", "description"], | ||||||
|                       viz.args.map((arg) => { |                     viz.args.map((arg) => { | ||||||
|                           let defaultArg = arg.defaultValue ?? "_undefined_" |                         let defaultArg = arg.defaultValue ?? "_undefined_" | ||||||
|                           if (defaultArg == "") { |                         if (defaultArg == "") { | ||||||
|                               defaultArg = "_empty string_" |                             defaultArg = "_empty string_" | ||||||
|                           } |                         } | ||||||
|                           return [arg.name, defaultArg, arg.doc] |                         return [arg.name, defaultArg, arg.doc] | ||||||
|                       }) |                     }), | ||||||
|                   ) |                 ) | ||||||
|                 : undefined, |                 : undefined, | ||||||
|             "#### Example usage of " + viz.funcName, |             "#### Example usage of " + viz.funcName, | ||||||
|             "<code>" + example + "</code>", |             "<code>" + example + "</code>", | ||||||
|  | @ -364,18 +364,18 @@ export default class SpecialVisualizations { | ||||||
| 
 | 
 | ||||||
|     public static constructSpecification( |     public static constructSpecification( | ||||||
|         template: string, |         template: string, | ||||||
|         extraMappings: SpecialVisualization[] = [] |         extraMappings: SpecialVisualization[] = [], | ||||||
|     ): RenderingSpecification[] { |     ): RenderingSpecification[] { | ||||||
|         return SpecialVisualisationUtils.constructSpecification( |         return SpecialVisualisationUtils.constructSpecification( | ||||||
|             template, |             template, | ||||||
|             SpecialVisualizations.specialVisualisationsDict, |             SpecialVisualizations.specialVisualisationsDict, | ||||||
|             extraMappings |             extraMappings, | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public static HelpMessage(): string { |     public static HelpMessage(): string { | ||||||
|         const helpTexts: string[] = SpecialVisualizations.specialVisualizations.map((viz) => |         const helpTexts: string[] = SpecialVisualizations.specialVisualizations.map((viz) => | ||||||
|             SpecialVisualizations.DocumentationFor(viz) |             SpecialVisualizations.DocumentationFor(viz), | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|         const firstPart = new Combine([ |         const firstPart = new Combine([ | ||||||
|  | @ -408,10 +408,10 @@ export default class SpecialVisualizations { | ||||||
|                         }, |                         }, | ||||||
|                     }, |                     }, | ||||||
|                     null, |                     null, | ||||||
|                     "  " |                     "  ", | ||||||
|                 ) |                 ), | ||||||
|             ).SetClass("code"), |             ).SetClass("code"), | ||||||
|             'In other words: use `{ "before": ..., "after": ..., "special": {"type": ..., "argname": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)', |             "In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)", | ||||||
|         ]) |         ]) | ||||||
|             .SetClass("flex flex-col") |             .SetClass("flex flex-col") | ||||||
|             .AsMarkdown() |             .AsMarkdown() | ||||||
|  | @ -452,7 +452,7 @@ export default class SpecialVisualizations { | ||||||
|                                     (ud) => ud?.languages ?? [] |                                     (ud) => ud?.languages ?? [] | ||||||
|                                 ), |                                 ), | ||||||
|                             }) |                             }) | ||||||
|                         }) |                         }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -491,7 +491,7 @@ export default class SpecialVisualizations { | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[], | ||||||
|                     feature: Feature |                     feature: Feature, | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource }) |                     return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource }) | ||||||
|                 }, |                 }, | ||||||
|  | @ -503,7 +503,7 @@ export default class SpecialVisualizations { | ||||||
| 
 | 
 | ||||||
|                 constr( |                 constr( | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tagSource: UIEventSource<Record<string, string>> |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|                         tagSource |                         tagSource | ||||||
|  | @ -513,7 +513,7 @@ export default class SpecialVisualizations { | ||||||
|                                     return new SvelteUIElement(SplitRoadWizard, { id, state }) |                                     return new SvelteUIElement(SplitRoadWizard, { id, state }) | ||||||
|                                 } |                                 } | ||||||
|                                 return undefined |                                 return undefined | ||||||
|                             }) |                             }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -527,7 +527,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     if (feature.geometry.type !== "Point") { |                     if (feature.geometry.type !== "Point") { | ||||||
|                         return undefined |                         return undefined | ||||||
|  | @ -550,7 +550,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     if (!layer.deletion) { |                     if (!layer.deletion) { | ||||||
|                         return undefined |                         return undefined | ||||||
|  | @ -576,7 +576,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ) { |                 ) { | ||||||
|                     if (feature.geometry.type !== "LineString") { |                     if (feature.geometry.type !== "LineString") { | ||||||
|                         return undefined |                         return undefined | ||||||
|  | @ -608,7 +608,7 @@ export default class SpecialVisualizations { | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature |                     feature: Feature, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const [lon, lat] = GeoOperations.centerpointCoordinates(feature) |                     const [lon, lat] = GeoOperations.centerpointCoordinates(feature) | ||||||
|                     return new SvelteUIElement(CreateNewNote, { |                     return new SvelteUIElement(CreateNewNote, { | ||||||
|  | @ -671,7 +671,7 @@ export default class SpecialVisualizations { | ||||||
|                             .map((tags) => tags[args[0]]) |                             .map((tags) => tags[args[0]]) | ||||||
|                             .map((wikidata) => { |                             .map((wikidata) => { | ||||||
|                                 wikidata = Utils.NoEmpty( |                                 wikidata = Utils.NoEmpty( | ||||||
|                                     wikidata?.split(";")?.map((wd) => wd.trim()) ?? [] |                                     wikidata?.split(";")?.map((wd) => wd.trim()) ?? [], | ||||||
|                                 )[0] |                                 )[0] | ||||||
|                                 const entry = Wikidata.LoadWikidataEntry(wikidata) |                                 const entry = Wikidata.LoadWikidataEntry(wikidata) | ||||||
|                                 return new VariableUiElement( |                                 return new VariableUiElement( | ||||||
|  | @ -681,9 +681,9 @@ export default class SpecialVisualizations { | ||||||
|                                         } |                                         } | ||||||
|                                         const response = <WikidataResponse>e["success"] |                                         const response = <WikidataResponse>e["success"] | ||||||
|                                         return Translation.fromMap(response.labels) |                                         return Translation.fromMap(response.labels) | ||||||
|                                     }) |                                     }), | ||||||
|                                 ) |                                 ) | ||||||
|                             }) |                             }), | ||||||
|                     ), |                     ), | ||||||
|             }, |             }, | ||||||
|             new MapillaryLinkVis(), |             new MapillaryLinkVis(), | ||||||
|  | @ -697,7 +697,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     _, |                     _, | ||||||
|                     __, |                     __, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ) => new SvelteUIElement(AllTagsPanel, { tags, layer }), |                 ) => new SvelteUIElement(AllTagsPanel, { tags, layer }), | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  | @ -782,7 +782,7 @@ export default class SpecialVisualizations { | ||||||
|                             nameKey: nameKey, |                             nameKey: nameKey, | ||||||
|                             fallbackName, |                             fallbackName, | ||||||
|                         }, |                         }, | ||||||
|                         state.featureSwitchIsTesting |                         state.featureSwitchIsTesting, | ||||||
|                     ) |                     ) | ||||||
|                     return new SvelteUIElement(StarsBarIcon, { |                     return new SvelteUIElement(StarsBarIcon, { | ||||||
|                         score: reviews.average, |                         score: reviews.average, | ||||||
|  | @ -821,7 +821,7 @@ export default class SpecialVisualizations { | ||||||
|                             nameKey: nameKey, |                             nameKey: nameKey, | ||||||
|                             fallbackName, |                             fallbackName, | ||||||
|                         }, |                         }, | ||||||
|                         state.featureSwitchIsTesting |                         state.featureSwitchIsTesting, | ||||||
|                     ) |                     ) | ||||||
|                     return new SvelteUIElement(ReviewForm, { |                     return new SvelteUIElement(ReviewForm, { | ||||||
|                         reviews, |                         reviews, | ||||||
|  | @ -859,7 +859,7 @@ export default class SpecialVisualizations { | ||||||
|                             nameKey: nameKey, |                             nameKey: nameKey, | ||||||
|                             fallbackName, |                             fallbackName, | ||||||
|                         }, |                         }, | ||||||
|                         state.featureSwitchIsTesting |                         state.featureSwitchIsTesting, | ||||||
|                     ) |                     ) | ||||||
|                     return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) |                     return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) | ||||||
|                 }, |                 }, | ||||||
|  | @ -889,7 +889,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new Combine([ |                     return new Combine([ | ||||||
|                         SpecialVisualizations.specialVisualisationsDict |                         SpecialVisualizations.specialVisualisationsDict | ||||||
|  | @ -914,7 +914,7 @@ export default class SpecialVisualizations { | ||||||
|                 constr( |                 constr( | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     _: UIEventSource<Record<string, string>>, |                     _: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[] |                     argument: string[], | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const [text] = argument |                     const [text] = argument | ||||||
|                     return new SvelteUIElement(ImportReviewIdentity, { state, text }) |                     return new SvelteUIElement(ImportReviewIdentity, { state, text }) | ||||||
|  | @ -971,7 +971,7 @@ export default class SpecialVisualizations { | ||||||
|                 constr( |                 constr( | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[] |                     args: string[], | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     const keyToUse = args[0] |                     const keyToUse = args[0] | ||||||
|                     const prefix = args[1] |                     const prefix = args[1] | ||||||
|  | @ -1008,17 +1008,17 @@ export default class SpecialVisualizations { | ||||||
|                                     return undefined |                                     return undefined | ||||||
|                                 } |                                 } | ||||||
|                                 const allUnits: Unit[] = [].concat( |                                 const allUnits: Unit[] = [].concat( | ||||||
|                                     ...(state?.theme?.layers?.map((lyr) => lyr.units) ?? []) |                                     ...(state?.theme?.layers?.map((lyr) => lyr.units) ?? []), | ||||||
|                                 ) |                                 ) | ||||||
|                                 const unit = allUnits.filter((unit) => |                                 const unit = allUnits.filter((unit) => | ||||||
|                                     unit.isApplicableToKey(key) |                                     unit.isApplicableToKey(key), | ||||||
|                                 )[0] |                                 )[0] | ||||||
|                                 if (unit === undefined) { |                                 if (unit === undefined) { | ||||||
|                                     return value |                                     return value | ||||||
|                                 } |                                 } | ||||||
|                                 const getCountry = () => tagSource.data._country |                                 const getCountry = () => tagSource.data._country | ||||||
|                                 return unit.asHumanLongValue(value, getCountry) |                                 return unit.asHumanLongValue(value, getCountry) | ||||||
|                             }) |                             }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1072,7 +1072,7 @@ export default class SpecialVisualizations { | ||||||
|                 constr: (state) => { |                 constr: (state) => { | ||||||
|                     return new SubtleButton( |                     return new SubtleButton( | ||||||
|                         new SvelteUIElement(Trash), |                         new SvelteUIElement(Trash), | ||||||
|                         Translations.t.general.removeLocationHistory |                         Translations.t.general.removeLocationHistory, | ||||||
|                     ).onClick(() => { |                     ).onClick(() => { | ||||||
|                         state.historicalUserLocations.features.setData([]) |                         state.historicalUserLocations.features.setData([]) | ||||||
|                         state.selectedElement.setData(undefined) |                         state.selectedElement.setData(undefined) | ||||||
|  | @ -1113,10 +1113,10 @@ export default class SpecialVisualizations { | ||||||
|                                                 new SvelteUIElement(NoteCommentElement, { |                                                 new SvelteUIElement(NoteCommentElement, { | ||||||
|                                                     comment, |                                                     comment, | ||||||
|                                                     state, |                                                     state, | ||||||
|                                                 }) |                                                 }), | ||||||
|                                         ) |                                         ), | ||||||
|                                 ).SetClass("flex flex-col") |                                 ).SetClass("flex flex-col") | ||||||
|                             }) |                             }), | ||||||
|                     ), |                     ), | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|  | @ -1149,7 +1149,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     _: string[], |                     _: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ) => { |                 ) => { | ||||||
|                     return new SvelteUIElement(FeatureTitle, { state, tags, feature, layer }) |                     return new SvelteUIElement(FeatureTitle, { state, tags, feature, layer }) | ||||||
|                 }, |                 }, | ||||||
|  | @ -1167,8 +1167,8 @@ export default class SpecialVisualizations { | ||||||
|                     const challenge = Stores.FromPromise( |                     const challenge = Stores.FromPromise( | ||||||
|                         Utils.downloadJsonCached<MaprouletteTask>( |                         Utils.downloadJsonCached<MaprouletteTask>( | ||||||
|                             `${Maproulette.defaultEndpoint}/challenge/${parentId}`, |                             `${Maproulette.defaultEndpoint}/challenge/${parentId}`, | ||||||
|                             24 * 60 * 60 * 1000 |                             24 * 60 * 60 * 1000, | ||||||
|                         ) |                         ), | ||||||
|                     ) |                     ) | ||||||
| 
 | 
 | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|  | @ -1193,7 +1193,7 @@ export default class SpecialVisualizations { | ||||||
|                             } else { |                             } else { | ||||||
|                                 return [title, new List(listItems)] |                                 return [title, new List(listItems)] | ||||||
|                             } |                             } | ||||||
|                         }) |                         }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|                 docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.", |                 docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.", | ||||||
|  | @ -1207,15 +1207,15 @@ export default class SpecialVisualizations { | ||||||
|                     "\n" + |                     "\n" + | ||||||
|                     "```json\n" + |                     "```json\n" + | ||||||
|                     "{\n" + |                     "{\n" + | ||||||
|                     '   "id": "mark_duplicate",\n' + |                     "   \"id\": \"mark_duplicate\",\n" + | ||||||
|                     '   "render": {\n' + |                     "   \"render\": {\n" + | ||||||
|                     '      "special": {\n' + |                     "      \"special\": {\n" + | ||||||
|                     '         "type": "maproulette_set_status",\n' + |                     "         \"type\": \"maproulette_set_status\",\n" + | ||||||
|                     '         "message": {\n' + |                     "         \"message\": {\n" + | ||||||
|                     '            "en": "Mark as not found or false positive"\n' + |                     "            \"en\": \"Mark as not found or false positive\"\n" + | ||||||
|                     "         },\n" + |                     "         },\n" + | ||||||
|                     '         "status": "2",\n' + |                     "         \"status\": \"2\",\n" + | ||||||
|                     '         "image": "close"\n' + |                     "         \"image\": \"close\"\n" + | ||||||
|                     "      }\n" + |                     "      }\n" + | ||||||
|                     "   }\n" + |                     "   }\n" + | ||||||
|                     "}\n" + |                     "}\n" + | ||||||
|  | @ -1291,7 +1291,7 @@ export default class SpecialVisualizations { | ||||||
|                                 (l) => |                                 (l) => | ||||||
|                                     l.name !== null && |                                     l.name !== null && | ||||||
|                                     l.title && |                                     l.title && | ||||||
|                                     state.perLayer.get(l.id) !== undefined |                                     state.perLayer.get(l.id) !== undefined, | ||||||
|                             ) |                             ) | ||||||
|                             .map( |                             .map( | ||||||
|                                 (l) => { |                                 (l) => { | ||||||
|  | @ -1301,8 +1301,8 @@ export default class SpecialVisualizations { | ||||||
|                                     const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) |                                     const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) | ||||||
|                                     return new StatisticsPanel(fsBboxed) |                                     return new StatisticsPanel(fsBboxed) | ||||||
|                                 }, |                                 }, | ||||||
|                                 [state.mapProperties.bounds] |                                 [state.mapProperties.bounds], | ||||||
|                             ) |                             ), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1372,7 +1372,7 @@ export default class SpecialVisualizations { | ||||||
|                 constr( |                 constr( | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[] |                     args: string[], | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     let [text, href, classnames, download, ariaLabel, icon] = args |                     let [text, href, classnames, download, ariaLabel, icon] = args | ||||||
|                     if (download === "") { |                     if (download === "") { | ||||||
|  | @ -1410,7 +1410,7 @@ export default class SpecialVisualizations { | ||||||
|                             }, |                             }, | ||||||
|                         }, |                         }, | ||||||
|                         null, |                         null, | ||||||
|                         "  " |                         "  ", | ||||||
|                     ) + |                     ) + | ||||||
|                     "\n```", |                     "\n```", | ||||||
|                 args: [ |                 args: [ | ||||||
|  | @ -1434,7 +1434,7 @@ export default class SpecialVisualizations { | ||||||
|                     featureTags: UIEventSource<Record<string, string>>, |                     featureTags: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ) { |                 ) { | ||||||
|                     const [key, tr, classesRaw] = args |                     const [key, tr, classesRaw] = args | ||||||
|                     let classes = classesRaw ?? "" |                     let classes = classesRaw ?? "" | ||||||
|  | @ -1452,7 +1452,7 @@ export default class SpecialVisualizations { | ||||||
|                                     "Could not create a special visualization for multi(", |                                     "Could not create a special visualization for multi(", | ||||||
|                                     args.join(", ") + ")", |                                     args.join(", ") + ")", | ||||||
|                                     "no properties found for object", |                                     "no properties found for object", | ||||||
|                                     feature.properties.id |                                     feature.properties.id, | ||||||
|                                 ) |                                 ) | ||||||
|                                 return undefined |                                 return undefined | ||||||
|                             } |                             } | ||||||
|  | @ -1469,7 +1469,7 @@ export default class SpecialVisualizations { | ||||||
|                                 elements.push(subsTr) |                                 elements.push(subsTr) | ||||||
|                             } |                             } | ||||||
|                             return elements |                             return elements | ||||||
|                         }) |                         }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1489,7 +1489,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|                         tagSource.map((tags) => { |                         tagSource.map((tags) => { | ||||||
|  | @ -1501,7 +1501,7 @@ export default class SpecialVisualizations { | ||||||
|                                 console.error("Cannot create a translation for", v, "due to", e) |                                 console.error("Cannot create a translation for", v, "due to", e) | ||||||
|                                 return JSON.stringify(v) |                                 return JSON.stringify(v) | ||||||
|                             } |                             } | ||||||
|                         }) |                         }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1521,7 +1521,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const key = argument[0] |                     const key = argument[0] | ||||||
|                     return new SvelteUIElement(FediverseLink, { key, tags, state }) |                     return new SvelteUIElement(FediverseLink, { key, tags, state }) | ||||||
|  | @ -1543,7 +1543,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new FixedUiElement("{" + args[0] + "}") |                     return new FixedUiElement("{" + args[0] + "}") | ||||||
|                 }, |                 }, | ||||||
|  | @ -1564,7 +1564,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const key = argument[0] ?? "value" |                     const key = argument[0] ?? "value" | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|  | @ -1582,12 +1582,12 @@ export default class SpecialVisualizations { | ||||||
|                             } catch (e) { |                             } catch (e) { | ||||||
|                                 return new FixedUiElement( |                                 return new FixedUiElement( | ||||||
|                                     "Could not parse this tag: " + |                                     "Could not parse this tag: " + | ||||||
|                                         JSON.stringify(value) + |                                     JSON.stringify(value) + | ||||||
|                                         " due to " + |                                     " due to " + | ||||||
|                                         e |                                     e, | ||||||
|                                 ).SetClass("alert") |                                 ).SetClass("alert") | ||||||
|                             } |                             } | ||||||
|                         }) |                         }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1608,7 +1608,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const giggityUrl = argument[0] |                     const giggityUrl = argument[0] | ||||||
|                     return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl }) |                     return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl }) | ||||||
|  | @ -1624,15 +1624,24 @@ export default class SpecialVisualizations { | ||||||
|                     _: UIEventSource<Record<string, string>>, |                     _: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const tags = (<ThemeViewState>( |                     const geostate = (<ThemeViewState>(state)).geolocation.geolocationState | ||||||
|                         state |                     const tags = geostate.currentGPSLocation.map( | ||||||
|                     )).geolocation.currentUserLocation.features.map( |                         (geoloc) => { | ||||||
|                         (features) => features[0]?.properties |                             const tags = { } | ||||||
|  |                             for (const k in geoloc ?? {}) { | ||||||
|  |                                 tags[k] = geoloc[k] | ||||||
|  |                             } | ||||||
|  |                             tags["_permission"] = geostate.permission.data, | ||||||
|  |                             tags["_request_moment"] = geostate.requestMoment.data | ||||||
|  |                             return tags | ||||||
|  |                         }, | ||||||
|  |                         [geostate.permission, geostate.requestMoment] | ||||||
|                     ) |                     ) | ||||||
|  | 
 | ||||||
|                     return new Combine([ |                     return new Combine([ | ||||||
|                         new SvelteUIElement(OrientationDebugPanel, {}), |                         new SvelteUIElement(OrientationDebugPanel, {}), // compass and gyroscope info
 | ||||||
|                         new SvelteUIElement(AllTagsPanel, { |                         new SvelteUIElement(AllTagsPanel, { | ||||||
|                             state, |                             state, | ||||||
|                             tags, |                             tags, | ||||||
|  | @ -1651,7 +1660,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new SvelteUIElement(MarkAsFavourite, { |                     return new SvelteUIElement(MarkAsFavourite, { | ||||||
|                         tags: tagSource, |                         tags: tagSource, | ||||||
|  | @ -1671,7 +1680,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new SvelteUIElement(MarkAsFavouriteMini, { |                     return new SvelteUIElement(MarkAsFavouriteMini, { | ||||||
|                         tags: tagSource, |                         tags: tagSource, | ||||||
|  | @ -1691,7 +1700,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new SvelteUIElement(DirectionIndicator, { state, feature }) |                     return new SvelteUIElement(DirectionIndicator, { state, feature }) | ||||||
|                 }, |                 }, | ||||||
|  | @ -1704,7 +1713,7 @@ export default class SpecialVisualizations { | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature |                     feature: Feature, | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     return new SvelteUIElement(QrCode, { state, tags, feature }) |                     return new SvelteUIElement(QrCode, { state, tags, feature }) | ||||||
|                 }, |                 }, | ||||||
|  | @ -1723,7 +1732,7 @@ export default class SpecialVisualizations { | ||||||
|                 constr( |                 constr( | ||||||
|                     state: SpecialVisualizationState, |                     state: SpecialVisualizationState, | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[] |                     args: string[], | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const key = args[0] === "" ? "_direction:centerpoint" : args[0] |                     const key = args[0] === "" ? "_direction:centerpoint" : args[0] | ||||||
|                     return new VariableUiElement( |                     return new VariableUiElement( | ||||||
|  | @ -1734,11 +1743,11 @@ export default class SpecialVisualizations { | ||||||
|                             }) |                             }) | ||||||
|                             .mapD((value) => { |                             .mapD((value) => { | ||||||
|                                 const dir = GeoOperations.bearingToHuman( |                                 const dir = GeoOperations.bearingToHuman( | ||||||
|                                     GeoOperations.parseBearing(value) |                                     GeoOperations.parseBearing(value), | ||||||
|                                 ) |                                 ) | ||||||
|                                 console.log("Human dir", dir) |                                 console.log("Human dir", dir) | ||||||
|                                 return Translations.t.general.visualFeedback.directionsAbsolute[dir] |                                 return Translations.t.general.visualFeedback.directionsAbsolute[dir] | ||||||
|                             }) |                             }), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1768,7 +1777,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const url = args[0] |                     const url = args[0] | ||||||
|                     const readonly = args[3] === "yes" |                     const readonly = args[3] === "yes" | ||||||
|  | @ -1794,12 +1803,12 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     args: string[], |                     args: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     return new Toggle( |                     return new Toggle( | ||||||
|                         undefined, |                         undefined, | ||||||
|                         new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }), |                         new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }), | ||||||
|                         state.osmConnection.isLoggedIn |                         state.osmConnection.isLoggedIn, | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1837,7 +1846,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const key = argument[0] ?? "website" |                     const key = argument[0] ?? "website" | ||||||
|                     const useProxy = argument[1] !== "no" |                     const useProxy = argument[1] !== "no" | ||||||
|  | @ -1845,7 +1854,7 @@ export default class SpecialVisualizations { | ||||||
|                     const isClosed = (argument[4] ?? "yes") === "yes" |                     const isClosed = (argument[4] ?? "yes") === "yes" | ||||||
| 
 | 
 | ||||||
|                     const countryStore: Store<string | undefined> = tags.mapD( |                     const countryStore: Store<string | undefined> = tags.mapD( | ||||||
|                         (tags) => tags._country |                         (tags) => tags._country, | ||||||
|                     ) |                     ) | ||||||
|                     const sourceUrl: Store<string | undefined> = tags.mapD((tags) => { |                     const sourceUrl: Store<string | undefined> = tags.mapD((tags) => { | ||||||
|                         if (!tags[key] || tags[key] === "undefined") { |                         if (!tags[key] || tags[key] === "undefined") { | ||||||
|  | @ -1867,24 +1876,24 @@ export default class SpecialVisualizations { | ||||||
|                                                 const features = |                                                 const features = | ||||||
|                                                     await LinkedDataLoader.fetchVeloparkEntry( |                                                     await LinkedDataLoader.fetchVeloparkEntry( | ||||||
|                                                         url, |                                                         url, | ||||||
|                                                         loadAll |                                                         loadAll, | ||||||
|                                                     ) |                                                     ) | ||||||
|                                                 const feature = |                                                 const feature = | ||||||
|                                                     features.find( |                                                     features.find( | ||||||
|                                                         (f) => f.properties["ref:velopark"] === url |                                                         (f) => f.properties["ref:velopark"] === url, | ||||||
|                                                     ) ?? features[0] |                                                     ) ?? features[0] | ||||||
|                                                 const properties = feature.properties |                                                 const properties = feature.properties | ||||||
|                                                 properties["ref:velopark"] = url |                                                 properties["ref:velopark"] = url | ||||||
|                                                 console.log( |                                                 console.log( | ||||||
|                                                     "Got properties from velopark:", |                                                     "Got properties from velopark:", | ||||||
|                                                     properties |                                                     properties, | ||||||
|                                                 ) |                                                 ) | ||||||
|                                                 return properties |                                                 return properties | ||||||
|                                             } catch (e) { |                                             } catch (e) { | ||||||
|                                                 console.error(e) |                                                 console.error(e) | ||||||
|                                                 throw e |                                                 throw e | ||||||
|                                             } |                                             } | ||||||
|                                         })() |                                         })(), | ||||||
|                                     ) |                                     ) | ||||||
|                                 } |                                 } | ||||||
|                                 if (country === undefined) { |                                 if (country === undefined) { | ||||||
|  | @ -1896,29 +1905,29 @@ export default class SpecialVisualizations { | ||||||
|                                             return await LinkedDataLoader.fetchJsonLd( |                                             return await LinkedDataLoader.fetchJsonLd( | ||||||
|                                                 url, |                                                 url, | ||||||
|                                                 { country }, |                                                 { country }, | ||||||
|                                                 useProxy ? "proxy" : "fetch-lod" |                                                 useProxy ? "proxy" : "fetch-lod", | ||||||
|                                             ) |                                             ) | ||||||
|                                         } catch (e) { |                                         } catch (e) { | ||||||
|                                             console.log( |                                             console.log( | ||||||
|                                                 "Could not get with proxy/download LOD, attempting to download directly. Error for ", |                                                 "Could not get with proxy/download LOD, attempting to download directly. Error for ", | ||||||
|                                                 url, |                                                 url, | ||||||
|                                                 "is", |                                                 "is", | ||||||
|                                                 e |                                                 e, | ||||||
|                                             ) |                                             ) | ||||||
|                                             return await LinkedDataLoader.fetchJsonLd( |                                             return await LinkedDataLoader.fetchJsonLd( | ||||||
|                                                 url, |                                                 url, | ||||||
|                                                 { country }, |                                                 { country }, | ||||||
|                                                 "fetch-raw" |                                                 "fetch-raw", | ||||||
|                                             ) |                                             ) | ||||||
|                                         } |                                         } | ||||||
|                                     })() |                                     })(), | ||||||
|                                 ) |                                 ) | ||||||
|                             }, |                             }, | ||||||
|                             [countryStore] |                             [countryStore], | ||||||
|                         ) |                         ) | ||||||
| 
 | 
 | ||||||
|                     externalData.addCallbackAndRunD((lod) => |                     externalData.addCallbackAndRunD((lod) => | ||||||
|                         console.log("linked_data_from_website received the following data:", lod) |                         console.log("linked_data_from_website received the following data:", lod), | ||||||
|                     ) |                     ) | ||||||
| 
 | 
 | ||||||
|                     return new Toggle( |                     return new Toggle( | ||||||
|  | @ -1933,7 +1942,7 @@ export default class SpecialVisualizations { | ||||||
|                             collapsed: isClosed, |                             collapsed: isClosed, | ||||||
|                         }), |                         }), | ||||||
|                         undefined, |                         undefined, | ||||||
|                         sourceUrl.map((url) => !!url) |                         sourceUrl.map((url) => !!url), | ||||||
|                     ) |                     ) | ||||||
|                 }, |                 }, | ||||||
|             }, |             }, | ||||||
|  | @ -1953,7 +1962,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const text = argument[0] |                     const text = argument[0] | ||||||
|                     const cssClasses = argument[1] |                     const cssClasses = argument[1] | ||||||
|  | @ -1975,7 +1984,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const translation = tagSource.map((tags) => { |                     const translation = tagSource.map((tags) => { | ||||||
|                         const layer = state.theme.getMatchingLayer(tags) |                         const layer = state.theme.getMatchingLayer(tags) | ||||||
|  | @ -2007,7 +2016,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     return new SvelteUIElement<any, any, any>(ClearCaches, { |                     return new SvelteUIElement<any, any, any>(ClearCaches, { | ||||||
|                         msg: argument[0] ?? "Clear local caches", |                         msg: argument[0] ?? "Clear local caches", | ||||||
|  | @ -2032,7 +2041,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     selectedElement: Feature, |                     selectedElement: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     const [header, labelsStr] = argument |                     const [header, labelsStr] = argument | ||||||
|                     const labels = labelsStr.split(";").map((x) => x.trim()) |                     const labels = labelsStr.split(";").map((x) => x.trim()) | ||||||
|  | @ -2055,7 +2064,7 @@ export default class SpecialVisualizations { | ||||||
|                     tags: UIEventSource<Record<string, string>>, |                     tags: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     selectedElement: Feature, |                     selectedElement: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): SvelteUIElement { |                 ): SvelteUIElement { | ||||||
|                     const t = Translations.t.preset_type |                     const t = Translations.t.preset_type | ||||||
|                     const question: QuestionableTagRenderingConfigJson = { |                     const question: QuestionableTagRenderingConfigJson = { | ||||||
|  | @ -2095,7 +2104,7 @@ export default class SpecialVisualizations { | ||||||
|                     tagSource: UIEventSource<Record<string, string>>, |                     tagSource: UIEventSource<Record<string, string>>, | ||||||
|                     argument: string[], |                     argument: string[], | ||||||
|                     feature: Feature, |                     feature: Feature, | ||||||
|                     layer: LayerConfig |                     layer: LayerConfig, | ||||||
|                 ): BaseUIElement { |                 ): BaseUIElement { | ||||||
|                     const text = argument[0] |                     const text = argument[0] | ||||||
|                     return new SubtleButton(undefined, text).onClick(() => { |                     return new SubtleButton(undefined, text).onClick(() => { | ||||||
|  | @ -2126,7 +2135,7 @@ export default class SpecialVisualizations { | ||||||
|                 "Invalid special visualisation found: funcName is undefined or doesn't match " + |                 "Invalid special visualisation found: funcName is undefined or doesn't match " + | ||||||
|                 regex + |                 regex + | ||||||
|                 invalid.map((sp) => sp.i).join(", ") + |                 invalid.map((sp) => sp.i).join(", ") + | ||||||
|                 '. Did you perhaps type \n  funcName: "funcname" // type declaration uses COLON\ninstead of:\n  funcName = "funcName" // value definition uses EQUAL' |                 ". Did you perhaps type \n  funcName: \"funcname\" // type declaration uses COLON\ninstead of:\n  funcName = \"funcName\" // value definition uses EQUAL" | ||||||
|             ) |             ) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ import MetaTagging from "./src/Logic/MetaTagging"; | ||||||
| import { FixedUiElement } from "./src/UI/Base/FixedUiElement"; | import { FixedUiElement } from "./src/UI/Base/FixedUiElement"; | ||||||
| import { Utils } from "./src/Utils" | import { Utils } from "./src/Utils" | ||||||
| import Constants from "./src/Models/Constants" | import Constants from "./src/Models/Constants" | ||||||
|  | import { AndroidPolyfill } from "./src/Logic/Web/AndroidPolyfill" | ||||||
| 
 | 
 | ||||||
| function webgl_support() { | function webgl_support() { | ||||||
|     try { |     try { | ||||||
|  |  | ||||||
|  | @ -19,5 +19,5 @@ | ||||||
|       "esModuleInterop": true |       "esModuleInterop": true | ||||||
|     } |     } | ||||||
|   }, |   }, | ||||||
|   "exclude": ["node_modules", "test", "scripts"] |   "exclude": ["node_modules", "test", "scripts","android","dist","dist-full"] | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -21,11 +21,21 @@ export default defineConfig({ | ||||||
|   build: { |   build: { | ||||||
|     rollupOptions: { |     rollupOptions: { | ||||||
|       input, |       input, | ||||||
|  |       external:[ | ||||||
|  |         "android" | ||||||
|  |       ] | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|   base: `${ASSET_URL}`, |   base: `${ASSET_URL}`, | ||||||
|   plugins , |   plugins , | ||||||
|   server: { |   server: { | ||||||
|     port: 1234, |     port: 1234, | ||||||
|  |     watch:{ | ||||||
|  |       ignored: [ | ||||||
|  |         "**/android/**", | ||||||
|  |         '**/.git/**', | ||||||
|  |         '**/dist/**' | ||||||
|  |       ] | ||||||
|  |     } | ||||||
|   }, |   }, | ||||||
| }) | }) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue