From 17b35e731e00b8412684bec676c94be9b841f2fc Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Sat, 24 Jul 2021 01:59:57 +0200 Subject: [PATCH] Formatting (pre-merge) --- Logic/SimpleMetaTagger.ts | 4 +- State.ts | 752 +++++++++++++++++++------------------- 2 files changed, 374 insertions(+), 382 deletions(-) diff --git a/Logic/SimpleMetaTagger.ts b/Logic/SimpleMetaTagger.ts index 233275bcc1..d1f61a62fb 100644 --- a/Logic/SimpleMetaTagger.ts +++ b/Logic/SimpleMetaTagger.ts @@ -96,10 +96,10 @@ export default class SimpleMetaTagger { const [, denomination] = unit.findDenomination(value) let canonical = denomination?.canonicalValue(value) ?? undefined; console.log("Rewritten ", key, " from", value, "into", canonical) - if(canonical === undefined && !unit.eraseInvalid) { + if (canonical === undefined && !unit.eraseInvalid) { break; } - + feature.properties[key] = canonical; break; } diff --git a/State.ts b/State.ts index c4169d50ff..2f1c81adb9 100644 --- a/State.ts +++ b/State.ts @@ -1,13 +1,13 @@ -import { Utils } from "./Utils"; -import { ElementStorage } from "./Logic/ElementStorage"; -import { Changes } from "./Logic/Osm/Changes"; -import { OsmConnection } from "./Logic/Osm/OsmConnection"; +import {Utils} from "./Utils"; +import {ElementStorage} from "./Logic/ElementStorage"; +import {Changes} from "./Logic/Osm/Changes"; +import {OsmConnection} from "./Logic/Osm/OsmConnection"; import Locale from "./UI/i18n/Locale"; -import { UIEventSource } from "./Logic/UIEventSource"; -import { LocalStorageSource } from "./Logic/Web/LocalStorageSource"; -import { QueryParameters } from "./Logic/Web/QueryParameters"; +import {UIEventSource} from "./Logic/UIEventSource"; +import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; +import {QueryParameters} from "./Logic/Web/QueryParameters"; import LayoutConfig from "./Customizations/JSON/LayoutConfig"; -import { MangroveIdentity } from "./Logic/Web/MangroveReviews"; +import {MangroveIdentity} from "./Logic/Web/MangroveReviews"; import InstalledThemes from "./Logic/Actors/InstalledThemes"; import BaseLayer from "./Models/BaseLayer"; import Loc from "./Models/Loc"; @@ -17,7 +17,7 @@ import OverpassFeatureSource from "./Logic/Actors/OverpassFeatureSource"; import LayerConfig from "./Customizations/JSON/LayerConfig"; import TitleHandler from "./Logic/Actors/TitleHandler"; import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; -import { Relation } from "./Logic/Osm/ExtractRelations"; +import {Relation} from "./Logic/Osm/ExtractRelations"; import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; /** @@ -25,402 +25,394 @@ import OsmApiFeatureSource from "./Logic/FeatureSource/OsmApiFeatureSource"; */ export default class State { - // The singleton of the global state - public static state: State; + // The singleton of the global state + public static state: State; - public readonly layoutToUse = new UIEventSource(undefined); + public readonly layoutToUse = new UIEventSource(undefined); - /** + /** The mapping from id -> UIEventSource */ - public allElements: ElementStorage; - /** + public allElements: ElementStorage; + /** THe change handler */ - public changes: Changes; - /** + public changes: Changes; + /** The leaflet instance of the big basemap */ - public leafletMap = new UIEventSource(undefined); - /** - * Background layer id - */ - public availableBackgroundLayers: UIEventSource; - /** + public leafletMap = new UIEventSource(undefined); + /** + * Background layer id + */ + public availableBackgroundLayers: UIEventSource; + /** The user credentials */ - public osmConnection: OsmConnection; + public osmConnection: OsmConnection; - public mangroveIdentity: MangroveIdentity; + public mangroveIdentity: MangroveIdentity; - public favouriteLayers: UIEventSource; + public favouriteLayers: UIEventSource; - public layerUpdater: OverpassFeatureSource; + public layerUpdater: OverpassFeatureSource; - public osmApiFeatureSource: OsmApiFeatureSource; + public osmApiFeatureSource: OsmApiFeatureSource; - public filteredLayers: UIEventSource< - { - readonly isDisplayed: UIEventSource; - readonly layerDef: LayerConfig; - }[] - > = new UIEventSource< - { - readonly isDisplayed: UIEventSource; - readonly layerDef: LayerConfig; - }[] - >([]); + public filteredLayers: UIEventSource<{ + readonly isDisplayed: UIEventSource; + readonly layerDef: LayerConfig; + }[]> = new UIEventSource<{ + readonly isDisplayed: UIEventSource; + readonly layerDef: LayerConfig; + }[]>([]); - /** + /** The latest element that was selected */ - public readonly selectedElement = new UIEventSource( - undefined, - "Selected element" - ); - - /** - * Keeps track of relations: which way is part of which other way? - * Set by the overpass-updater; used in the metatagging - */ - public readonly knownRelations = new UIEventSource< - Map - >(undefined, "Relation memberships"); - - public readonly featureSwitchUserbadge: UIEventSource; - public readonly featureSwitchSearch: UIEventSource; - public readonly featureSwitchLayers: UIEventSource; - public readonly featureSwitchAddNew: UIEventSource; - public readonly featureSwitchWelcomeMessage: UIEventSource; - public readonly featureSwitchIframe: UIEventSource; - public readonly featureSwitchMoreQuests: UIEventSource; - public readonly featureSwitchShareScreen: UIEventSource; - public readonly featureSwitchGeolocation: UIEventSource; - public readonly featureSwitchIsTesting: UIEventSource; - public readonly featureSwitchIsDebugging: UIEventSource; - public readonly featureSwitchShowAllQuestions: UIEventSource; - public readonly featureSwitchApiURL: UIEventSource; - public readonly featureSwitchFilter: UIEventSource; - - /** - * The map location: currently centered lat, lon and zoom - */ - public readonly locationControl = new UIEventSource(undefined); - public backgroundLayer; - public readonly backgroundLayerId: UIEventSource; - - /* Last location where a click was registered - */ - public readonly LastClickLocation: UIEventSource<{ - lat: number; - lon: number; - }> = new UIEventSource<{ lat: number; lon: number }>(undefined); - - /** - * The location as delivered by the GPS - */ - public currentGPSLocation: UIEventSource<{ - latlng: { lat: number; lng: number }; - accuracy: number; - }> = new UIEventSource<{ - latlng: { lat: number; lng: number }; - accuracy: number; - }>(undefined); - public layoutDefinition: string; - public installedThemes: UIEventSource< - { layout: LayoutConfig; definition: string }[] - >; - - public layerControlIsOpened: UIEventSource = - QueryParameters.GetQueryParameter( - "layer-control-toggle", - "false", - "Whether or not the layer control is shown" - ).map( - (str) => str !== "false", - [], - (b) => "" + b - ); - - public FilterIsOpened: UIEventSource = - QueryParameters.GetQueryParameter( - "filter-toggle", - "false", - "Whether or not the filter is shown" - ).map( - (str) => str !== "false", - [], - (b) => "" + b - ); - - public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter( - "tab", - "0", - `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)` - ).map( - (str) => (isNaN(Number(str)) ? 0 : Number(str)), - [], - (n) => "" + n - ); - - constructor(layoutToUse: LayoutConfig) { - const self = this; - - this.layoutToUse.setData(layoutToUse); - - // -- Location control initialization - { - const zoom = State.asFloat( - QueryParameters.GetQueryParameter( - "z", - "" + (layoutToUse?.startZoom ?? 1), - "The initial/current zoom level" - ).syncWith(LocalStorageSource.Get("zoom")) - ); - const lat = State.asFloat( - QueryParameters.GetQueryParameter( - "lat", - "" + (layoutToUse?.startLat ?? 0), - "The initial/current latitude" - ).syncWith(LocalStorageSource.Get("lat")) - ); - const lon = State.asFloat( - QueryParameters.GetQueryParameter( - "lon", - "" + (layoutToUse?.startLon ?? 0), - "The initial/current longitude of the app" - ).syncWith(LocalStorageSource.Get("lon")) - ); - - this.locationControl = new UIEventSource({ - zoom: Utils.asFloat(zoom.data), - lat: Utils.asFloat(lat.data), - lon: Utils.asFloat(lon.data), - }).addCallback((latlonz) => { - zoom.setData(latlonz.zoom); - lat.setData(latlonz.lat); - lon.setData(latlonz.lon); - }); - - this.layoutToUse.addCallback((layoutToUse) => { - const lcd = self.locationControl.data; - lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; - lcd.lat = lcd.lat ?? layoutToUse?.startLat; - lcd.lon = lcd.lon ?? layoutToUse?.startLon; - self.locationControl.ping(); - }); - } - - // Helper function to initialize feature switches - function featSw( - key: string, - deflt: (layout: LayoutConfig) => boolean, - documentation: string - ): UIEventSource { - const queryParameterSource = QueryParameters.GetQueryParameter( - key, + public readonly selectedElement = new UIEventSource( undefined, - documentation - ); - // I'm so sorry about someone trying to decipher this - - // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened - return UIEventSource.flatten( - self.layoutToUse.map((layout) => { - const defaultValue = deflt(layout); - const queryParam = QueryParameters.GetQueryParameter( - key, - "" + defaultValue, - documentation - ); - return queryParam.map((str) => - str === undefined ? defaultValue : str !== "false" - ); - }), - [queryParameterSource] - ); - } - - // Feature switch initialization - not as a function as the UIEventSources are readonly - { - this.featureSwitchUserbadge = featSw( - "fs-userbadge", - (layoutToUse) => layoutToUse?.enableUserBadge ?? true, - "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode." - ); - this.featureSwitchSearch = featSw( - "fs-search", - (layoutToUse) => layoutToUse?.enableSearch ?? true, - "Disables/Enables the search bar" - ); - this.featureSwitchLayers = featSw( - "fs-layers", - (layoutToUse) => layoutToUse?.enableLayers ?? true, - "Disables/Enables the layer control" - ); - this.featureSwitchFilter = featSw( - "fs-filter", - (layoutToUse) => layoutToUse?.enableLayers ?? true, - "Disables/Enables the filter" - ); - this.featureSwitchAddNew = featSw( - "fs-add-new", - (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, - "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)" - ); - this.featureSwitchWelcomeMessage = featSw( - "fs-welcome-message", - () => true, - "Disables/enables the help menu or welcome message" - ); - this.featureSwitchIframe = featSw( - "fs-iframe", - () => false, - "Disables/Enables the iframe-popup" - ); - this.featureSwitchMoreQuests = featSw( - "fs-more-quests", - (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, - "Disables/Enables the 'More Quests'-tab in the welcome message" - ); - this.featureSwitchShareScreen = featSw( - "fs-share-screen", - (layoutToUse) => layoutToUse?.enableShareScreen ?? true, - "Disables/Enables the 'Share-screen'-tab in the welcome message" - ); - this.featureSwitchGeolocation = featSw( - "fs-geolocation", - (layoutToUse) => layoutToUse?.enableGeolocation ?? true, - "Disables/Enables the geolocation button" - ); - this.featureSwitchShowAllQuestions = featSw( - "fs-all-questions", - (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false, - "Always show all questions" - ); - - this.featureSwitchIsTesting = QueryParameters.GetQueryParameter( - "test", - "false", - "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org" - ).map( - (str) => str === "true", - [], - (b) => "" + b - ); - - this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter( - "debug", - "false", - "If true, shows some extra debugging help such as all the available tags on every object" - ).map( - (str) => str === "true", - [], - (b) => "" + b - ); - - this.featureSwitchApiURL = QueryParameters.GetQueryParameter( - "backend", - "osm", - "The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'" - ); - } - { - // Some other feature switches - const customCssQP = QueryParameters.GetQueryParameter( - "custom-css", - "", - "If specified, the custom css from the given link will be loaded additionaly" - ); - if (customCssQP.data !== undefined && customCssQP.data !== "") { - Utils.LoadCustomCss(customCssQP.data); - } - - this.backgroundLayerId = QueryParameters.GetQueryParameter( - "background", - layoutToUse?.defaultBackgroundId ?? "osm", - "The id of the background layer to start with" - ); - } - - if (Utils.runningFromConsole) { - return; - } - - this.osmConnection = new OsmConnection( - this.featureSwitchIsTesting.data, - QueryParameters.GetQueryParameter( - "oauth_token", - undefined, - "Used to complete the login" - ), - layoutToUse?.id, - true, - // @ts-ignore - this.featureSwitchApiURL.data + "Selected element" ); - this.allElements = new ElementStorage(); - this.changes = new Changes(); - this.osmApiFeatureSource = new OsmApiFeatureSource(); + /** + * Keeps track of relations: which way is part of which other way? + * Set by the overpass-updater; used in the metatagging + */ + public readonly knownRelations = new UIEventSource>(undefined, "Relation memberships"); - new PendingChangesUploader(this.changes, this.selectedElement); + public readonly featureSwitchUserbadge: UIEventSource; + public readonly featureSwitchSearch: UIEventSource; + public readonly featureSwitchLayers: UIEventSource; + public readonly featureSwitchAddNew: UIEventSource; + public readonly featureSwitchWelcomeMessage: UIEventSource; + public readonly featureSwitchIframe: UIEventSource; + public readonly featureSwitchMoreQuests: UIEventSource; + public readonly featureSwitchShareScreen: UIEventSource; + public readonly featureSwitchGeolocation: UIEventSource; + public readonly featureSwitchIsTesting: UIEventSource; + public readonly featureSwitchIsDebugging: UIEventSource; + public readonly featureSwitchShowAllQuestions: UIEventSource; + public readonly featureSwitchApiURL: UIEventSource; + public readonly featureSwitchFilter: UIEventSource; - this.mangroveIdentity = new MangroveIdentity( - this.osmConnection.GetLongPreference("identity", "mangrove") - ); + /** + * The map location: currently centered lat, lon and zoom + */ + public readonly locationControl = new UIEventSource(undefined); + public backgroundLayer; + public readonly backgroundLayerId: UIEventSource; - this.installedThemes = new InstalledThemes( - this.osmConnection - ).installedThemes; + /* Last location where a click was registered + */ + public readonly LastClickLocation: UIEventSource<{ + lat: number; + lon: number; + }> = new UIEventSource<{ lat: number; lon: number }>(undefined); - // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme - this.favouriteLayers = LocalStorageSource.Get("favouriteLayers") - .syncWith(this.osmConnection.GetLongPreference("favouriteLayers")) - .map( - (str) => Utils.Dedup(str?.split(";")) ?? [], + /** + * The location as delivered by the GPS + */ + public currentGPSLocation: UIEventSource<{ + latlng: { lat: number; lng: number }; + accuracy: number; + }> = new UIEventSource<{ + latlng: { lat: number; lng: number }; + accuracy: number; + }>(undefined); + public layoutDefinition: string; + public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>; + + public layerControlIsOpened: UIEventSource = + QueryParameters.GetQueryParameter( + "layer-control-toggle", + "false", + "Whether or not the layer control is shown" + ).map( + (str) => str !== "false", + [], + (b) => "" + b + ); + + public FilterIsOpened: UIEventSource = + QueryParameters.GetQueryParameter( + "filter-toggle", + "false", + "Whether or not the filter is shown" + ).map( + (str) => str !== "false", + [], + (b) => "" + b + ); + + public welcomeMessageOpenedTab = QueryParameters.GetQueryParameter( + "tab", + "0", + `The tab that is shown in the welcome-message. 0 = the explanation of the theme,1 = OSM-credits, 2 = sharescreen, 3 = more themes, 4 = about mapcomplete (user must be logged in and have >${Constants.userJourney.mapCompleteHelpUnlock} changesets)` + ).map( + (str) => (isNaN(Number(str)) ? 0 : Number(str)), [], - (layers) => Utils.Dedup(layers)?.join(";") - ); - - Locale.language.syncWith(this.osmConnection.GetPreference("language")); - - Locale.language - .addCallback((currentLanguage) => { - const layoutToUse = self.layoutToUse.data; - if (layoutToUse === undefined) { - return; - } - if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) { - console.log( - "Resetting language to", - layoutToUse.language[0], - "as", - currentLanguage, - " is unsupported" - ); - // The current language is not supported -> switch to a supported one - Locale.language.setData(layoutToUse.language[0]); - } - }) - .ping(); - - new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); - } - - private static asFloat(source: UIEventSource): UIEventSource { - return source.map( - (str) => { - let parsed = parseFloat(str); - return isNaN(parsed) ? undefined : parsed; - }, - [], - (fl) => { - if (fl === undefined || isNaN(fl)) { - return undefined; - } - return ("" + fl).substr(0, 8); - } + (n) => "" + n ); - } + + constructor(layoutToUse: LayoutConfig) { + const self = this; + + this.layoutToUse.setData(layoutToUse); + + // -- Location control initialization + { + const zoom = State.asFloat( + QueryParameters.GetQueryParameter( + "z", + "" + (layoutToUse?.startZoom ?? 1), + "The initial/current zoom level" + ).syncWith(LocalStorageSource.Get("zoom")) + ); + const lat = State.asFloat( + QueryParameters.GetQueryParameter( + "lat", + "" + (layoutToUse?.startLat ?? 0), + "The initial/current latitude" + ).syncWith(LocalStorageSource.Get("lat")) + ); + const lon = State.asFloat( + QueryParameters.GetQueryParameter( + "lon", + "" + (layoutToUse?.startLon ?? 0), + "The initial/current longitude of the app" + ).syncWith(LocalStorageSource.Get("lon")) + ); + + this.locationControl = new UIEventSource({ + zoom: Utils.asFloat(zoom.data), + lat: Utils.asFloat(lat.data), + lon: Utils.asFloat(lon.data), + }).addCallback((latlonz) => { + zoom.setData(latlonz.zoom); + lat.setData(latlonz.lat); + lon.setData(latlonz.lon); + }); + + this.layoutToUse.addCallback((layoutToUse) => { + const lcd = self.locationControl.data; + lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom; + lcd.lat = lcd.lat ?? layoutToUse?.startLat; + lcd.lon = lcd.lon ?? layoutToUse?.startLon; + self.locationControl.ping(); + }); + } + + // Helper function to initialize feature switches + function featSw( + key: string, + deflt: (layout: LayoutConfig) => boolean, + documentation: string + ): UIEventSource { + const queryParameterSource = QueryParameters.GetQueryParameter( + key, + undefined, + documentation + ); + // I'm so sorry about someone trying to decipher this + + // It takes the current layout, extracts the default value for this query parameter. A query parameter event source is then retrieved and flattened + return UIEventSource.flatten( + self.layoutToUse.map((layout) => { + const defaultValue = deflt(layout); + const queryParam = QueryParameters.GetQueryParameter( + key, + "" + defaultValue, + documentation + ); + return queryParam.map((str) => + str === undefined ? defaultValue : str !== "false" + ); + }), + [queryParameterSource] + ); + } + + // Feature switch initialization - not as a function as the UIEventSources are readonly + { + this.featureSwitchUserbadge = featSw( + "fs-userbadge", + (layoutToUse) => layoutToUse?.enableUserBadge ?? true, + "Disables/Enables the user information pill (userbadge) at the top left. Disabling this disables logging in and thus disables editing all together, effectively putting MapComplete into read-only mode." + ); + this.featureSwitchSearch = featSw( + "fs-search", + (layoutToUse) => layoutToUse?.enableSearch ?? true, + "Disables/Enables the search bar" + ); + this.featureSwitchLayers = featSw( + "fs-layers", + (layoutToUse) => layoutToUse?.enableLayers ?? true, + "Disables/Enables the layer control" + ); + this.featureSwitchFilter = featSw( + "fs-filter", + (layoutToUse) => layoutToUse?.enableLayers ?? true, + "Disables/Enables the filter" + ); + this.featureSwitchAddNew = featSw( + "fs-add-new", + (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true, + "Disables/Enables the 'add new feature'-popup. (A theme without presets might not have it in the first place)" + ); + this.featureSwitchWelcomeMessage = featSw( + "fs-welcome-message", + () => true, + "Disables/enables the help menu or welcome message" + ); + this.featureSwitchIframe = featSw( + "fs-iframe", + () => false, + "Disables/Enables the iframe-popup" + ); + this.featureSwitchMoreQuests = featSw( + "fs-more-quests", + (layoutToUse) => layoutToUse?.enableMoreQuests ?? true, + "Disables/Enables the 'More Quests'-tab in the welcome message" + ); + this.featureSwitchShareScreen = featSw( + "fs-share-screen", + (layoutToUse) => layoutToUse?.enableShareScreen ?? true, + "Disables/Enables the 'Share-screen'-tab in the welcome message" + ); + this.featureSwitchGeolocation = featSw( + "fs-geolocation", + (layoutToUse) => layoutToUse?.enableGeolocation ?? true, + "Disables/Enables the geolocation button" + ); + this.featureSwitchShowAllQuestions = featSw( + "fs-all-questions", + (layoutToUse) => layoutToUse?.enableShowAllQuestions ?? false, + "Always show all questions" + ); + + this.featureSwitchIsTesting = QueryParameters.GetQueryParameter( + "test", + "false", + "If true, 'dryrun' mode is activated. The app will behave as normal, except that changes to OSM will be printed onto the console instead of actually uploaded to osm.org" + ).map( + (str) => str === "true", + [], + (b) => "" + b + ); + + this.featureSwitchIsDebugging = QueryParameters.GetQueryParameter( + "debug", + "false", + "If true, shows some extra debugging help such as all the available tags on every object" + ).map( + (str) => str === "true", + [], + (b) => "" + b + ); + + this.featureSwitchApiURL = QueryParameters.GetQueryParameter( + "backend", + "osm", + "The OSM backend to use - can be used to redirect mapcomplete to the testing backend when using 'osm-test'" + ); + } + { + // Some other feature switches + const customCssQP = QueryParameters.GetQueryParameter( + "custom-css", + "", + "If specified, the custom css from the given link will be loaded additionaly" + ); + if (customCssQP.data !== undefined && customCssQP.data !== "") { + Utils.LoadCustomCss(customCssQP.data); + } + + this.backgroundLayerId = QueryParameters.GetQueryParameter( + "background", + layoutToUse?.defaultBackgroundId ?? "osm", + "The id of the background layer to start with" + ); + } + + if (Utils.runningFromConsole) { + return; + } + + this.osmConnection = new OsmConnection( + this.featureSwitchIsTesting.data, + QueryParameters.GetQueryParameter( + "oauth_token", + undefined, + "Used to complete the login" + ), + layoutToUse?.id, + true, + // @ts-ignore + this.featureSwitchApiURL.data + ); + + this.allElements = new ElementStorage(); + this.changes = new Changes(); + this.osmApiFeatureSource = new OsmApiFeatureSource(); + + new PendingChangesUploader(this.changes, this.selectedElement); + + this.mangroveIdentity = new MangroveIdentity( + this.osmConnection.GetLongPreference("identity", "mangrove") + ); + + this.installedThemes = new InstalledThemes( + this.osmConnection + ).installedThemes; + + // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme + this.favouriteLayers = LocalStorageSource.Get("favouriteLayers") + .syncWith(this.osmConnection.GetLongPreference("favouriteLayers")) + .map( + (str) => Utils.Dedup(str?.split(";")) ?? [], + [], + (layers) => Utils.Dedup(layers)?.join(";") + ); + + Locale.language.syncWith(this.osmConnection.GetPreference("language")); + + Locale.language + .addCallback((currentLanguage) => { + const layoutToUse = self.layoutToUse.data; + if (layoutToUse === undefined) { + return; + } + if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) { + console.log( + "Resetting language to", + layoutToUse.language[0], + "as", + currentLanguage, + " is unsupported" + ); + // The current language is not supported -> switch to a supported one + Locale.language.setData(layoutToUse.language[0]); + } + }) + .ping(); + + new TitleHandler(this.layoutToUse, this.selectedElement, this.allElements); + } + + private static asFloat(source: UIEventSource): UIEventSource { + return source.map( + (str) => { + let parsed = parseFloat(str); + return isNaN(parsed) ? undefined : parsed; + }, + [], + (fl) => { + if (fl === undefined || isNaN(fl)) { + return undefined; + } + return ("" + fl).substr(0, 8); + } + ); + } }