forked from MapComplete/MapComplete
		
	Stabilize personal theme, textfield now correctly appears if it is an option in the freeform too
This commit is contained in:
		
							parent
							
								
									79fc3f54e5
								
							
						
					
					
						commit
						416a76ae4f
					
				
					 22 changed files with 278 additions and 149 deletions
				
			
		|  | @ -74,15 +74,8 @@ export class InitUiElements { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         InitUiElements.InitBaseMap(); |  | ||||||
| 
 |  | ||||||
|         InitUiElements.setupAllLayerElements(); |  | ||||||
| 
 |  | ||||||
|         if (layoutToUse.customCss !== undefined) { |  | ||||||
|             Utils.LoadCustomCss(layoutToUse.customCss); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         function updateFavs() { |         function updateFavs() { | ||||||
|  |             // This is purely for the personal theme to load the layers there
 | ||||||
|             const favs = State.state.favouriteLayers.data ?? []; |             const favs = State.state.favouriteLayers.data ?? []; | ||||||
| 
 | 
 | ||||||
|             layoutToUse.layers.splice(0, layoutToUse.layers.length); |             layoutToUse.layers.splice(0, layoutToUse.layers.length); | ||||||
|  | @ -103,19 +96,16 @@ export class InitUiElements { | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| 
 |  | ||||||
|             InitUiElements.setupAllLayerElements(); |  | ||||||
|             State.state.layerUpdater.ForceRefresh(); |  | ||||||
|             State.state.layoutToUse.ping(); |             State.state.layoutToUse.ping(); | ||||||
| 
 |             State.state.layerUpdater?.ForceRefresh(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         if (layoutToUse.id === personal.id) { |         if (layoutToUse.customCss !== undefined) { | ||||||
|             State.state.favouriteLayers.addCallback(updateFavs); |             Utils.LoadCustomCss(layoutToUse.customCss); | ||||||
|             State.state.installedThemes.addCallback(updateFavs); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         InitUiElements.InitBaseMap(); | ||||||
| 
 | 
 | ||||||
|         InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => { |         InitUiElements.OnlyIf(State.state.featureSwitchUserbadge, () => { | ||||||
|             new UserBadge().AttachTo('userbadge'); |             new UserBadge().AttachTo('userbadge'); | ||||||
|  | @ -162,7 +152,17 @@ export class InitUiElements { | ||||||
|             , State.state.featureSwitchGeolocation) |             , State.state.featureSwitchGeolocation) | ||||||
|             .AttachTo("geolocate-button"); |             .AttachTo("geolocate-button"); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |         updateFavs(); | ||||||
|  |         InitUiElements.setupAllLayerElements(); | ||||||
|  |          | ||||||
|  |         if (layoutToUse.id === personal.id) { | ||||||
|  |             State.state.favouriteLayers.addCallback(updateFavs); | ||||||
|  |             State.state.installedThemes.addCallback(updateFavs); | ||||||
|  |         }else{ | ||||||
|             State.state.locationControl.ping(); |             State.state.locationControl.ping(); | ||||||
|  |         } | ||||||
|  |          | ||||||
|         // Reset the loading message once things are loaded
 |         // Reset the loading message once things are loaded
 | ||||||
|         new CenterMessageBox().AttachTo("centermessage"); |         new CenterMessageBox().AttachTo("centermessage"); | ||||||
| 
 | 
 | ||||||
|  | @ -209,7 +209,6 @@ export class InitUiElements { | ||||||
| 
 | 
 | ||||||
|         const isOpened = new UIEventSource<boolean>(true); |         const isOpened = new UIEventSource<boolean>(true); | ||||||
|         const fullOptions = new FullWelcomePaneWithTabs(() => { |         const fullOptions = new FullWelcomePaneWithTabs(() => { | ||||||
|             console.log("Closing the welcome message...") |  | ||||||
|             isOpened.setData(false); |             isOpened.setData(false); | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | @ -325,7 +324,7 @@ export class InitUiElements { | ||||||
| 
 | 
 | ||||||
|         const updater = new LoadFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap); |         const updater = new LoadFromOverpass(state.locationControl, state.layoutToUse, state.leafletMap); | ||||||
|         State.state.layerUpdater = updater; |         State.state.layerUpdater = updater; | ||||||
|         const source = new FeaturePipeline(state.filteredLayers.data, updater, state.layoutToUse, state.changes, state.locationControl); |         const source = new FeaturePipeline(state.filteredLayers, updater, state.layoutToUse, state.changes, state.locationControl); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|         source.features.addCallbackAndRun((featuresFreshness: { feature: any, freshness: Date }[]) => { |         source.features.addCallbackAndRun((featuresFreshness: { feature: any, freshness: Date }[]) => { | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ export default class InstalledThemes { | ||||||
|                 return installedThemes; |                 return installedThemes; | ||||||
|             } |             } | ||||||
|             const invalidThemes = [] |             const invalidThemes = [] | ||||||
|             for (var allPreferencesKey in allPreferences) { |             for (const allPreferencesKey in allPreferences) { | ||||||
|                 const themename = allPreferencesKey.match(/^mapcomplete-installed-theme-(.*)-combined-length$/); |                 const themename = allPreferencesKey.match(/^mapcomplete-installed-theme-(.*)-combined-length$/); | ||||||
|                 if (themename && themename[1] !== "") { |                 if (themename && themename[1] !== "") { | ||||||
|                     const customLayout = osmConnection.GetLongPreference("installed-theme-" + themename[1]); |                     const customLayout = osmConnection.GetLongPreference("installed-theme-" + themename[1]); | ||||||
|  |  | ||||||
|  | @ -44,10 +44,15 @@ export default class SelectedFeatureHandler { | ||||||
|             // Feature already selected
 |             // Feature already selected
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         const hash = this._hash.data; | ||||||
|  |         if(hash === undefined || hash === "" || hash === "#"){ | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|         console.log("Selecting a feature from the hash...") |         console.log("Selecting a feature from the hash...") | ||||||
|         for (const feature of features) { |         for (const feature of features) { | ||||||
|             const id = feature.feature?.properties?.id; |             const id = feature.feature?.properties?.id; | ||||||
|             if(id === this._hash.data){ |             if(id === hash){ | ||||||
|                 this._selectedFeature.setData(feature.feature); |                 this._selectedFeature.setData(feature.feature); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -7,18 +7,18 @@ import Bounds from "../../Models/Bounds"; | ||||||
| import FeatureSource from "../FeatureSource/FeatureSource"; | import FeatureSource from "../FeatureSource/FeatureSource"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export default class UpdateFromOverpass implements FeatureSource{ | export default class UpdateFromOverpass implements FeatureSource { | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * The last loaded features of the geojson |      * The last loaded features of the geojson | ||||||
|      */ |      */ | ||||||
|     public readonly features: UIEventSource<{feature:any, freshness: Date}[]> = new UIEventSource<any[]>(undefined); |     public readonly features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<any[]>(undefined); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     public readonly sufficientlyZoomed: UIEventSource<boolean>; |     public readonly sufficientlyZoomed: UIEventSource<boolean>; | ||||||
|     public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false); |     public readonly runningQuery: UIEventSource<boolean> = new UIEventSource<boolean>(false); | ||||||
|     public readonly retries: UIEventSource<number> = new UIEventSource<number>(0); |     public readonly timeout: UIEventSource<number> = new UIEventSource<number>(0); | ||||||
|      |     private readonly retries: UIEventSource<number> = new UIEventSource<number>(0); | ||||||
|     /** |     /** | ||||||
|      * The previous bounds for which the query has been run at the given zoom level |      * The previous bounds for which the query has been run at the given zoom level | ||||||
|      * |      * | ||||||
|  | @ -44,7 +44,7 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|         const self = this; |         const self = this; | ||||||
| 
 | 
 | ||||||
|         this.sufficientlyZoomed = location.map(location => { |         this.sufficientlyZoomed = location.map(location => { | ||||||
|                 if(location?.zoom === undefined){ |                 if (location?.zoom === undefined) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); |                 let minzoom = Math.min(...layoutToUse.data.layers.map(layer => layer.minzoom ?? 18)); | ||||||
|  | @ -59,7 +59,7 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|         layoutToUse.addCallback(() => { |         layoutToUse.addCallback(() => { | ||||||
|             self.update() |             self.update() | ||||||
|         }); |         }); | ||||||
|         location.addCallbackAndRun(() => { |         location.addCallback(() => { | ||||||
|             self.update() |             self.update() | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | @ -74,13 +74,13 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|     private GetFilter() { |     private GetFilter() { | ||||||
|         const filters: TagsFilter[] = []; |         const filters: TagsFilter[] = []; | ||||||
|         for (const layer of this._layoutToUse.data.layers) { |         for (const layer of this._layoutToUse.data.layers) { | ||||||
|             if(typeof(layer) === "string"){ |             if (typeof (layer) === "string") { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             if (this._location.data.zoom < layer.minzoom) { |             if (this._location.data.zoom < layer.minzoom) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             if(layer.doNotDownload){ |             if (layer.doNotDownload) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -94,7 +94,7 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|                 } |                 } | ||||||
|                 for (const previousLoadedBound of previousLoadedBounds) { |                 for (const previousLoadedBound of previousLoadedBounds) { | ||||||
|                     previouslyLoaded = previouslyLoaded || this.IsInBounds(previousLoadedBound); |                     previouslyLoaded = previouslyLoaded || this.IsInBounds(previousLoadedBound); | ||||||
|                     if(previouslyLoaded){ |                     if (previouslyLoaded) { | ||||||
|                         break; |                         break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | @ -109,6 +109,7 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|         } |         } | ||||||
|         return new Or(filters); |         return new Or(filters); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     private update(): void { |     private update(): void { | ||||||
|         const filter = this.GetFilter(); |         const filter = this.GetFilter(); | ||||||
|         if (filter === undefined) { |         if (filter === undefined) { | ||||||
|  | @ -145,21 +146,36 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|             function (reason) { |             function (reason) { | ||||||
|                 self.retries.data++; |                 self.retries.data++; | ||||||
|                 self.ForceRefresh(); |                 self.ForceRefresh(); | ||||||
|                 console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec)`, undefined); |                 self.timeout.setData(self.retries.data * 5); | ||||||
|  |                 console.log(`QUERY FAILED (retrying in ${5 * self.retries.data} sec)`, reason); | ||||||
|                 self.retries.ping(); |                 self.retries.ping(); | ||||||
|                 self.runningQuery.setData(false) |                 self.runningQuery.setData(false); | ||||||
|  | 
 | ||||||
|  |                 function countDown() { | ||||||
|                     window?.setTimeout( |                     window?.setTimeout( | ||||||
|                         function () { |                         function () { | ||||||
|                         self.update() |                             console.log("Countdown: ", self.timeout.data) | ||||||
|                     }, self.retries.data * 5000 |                             if (self.timeout.data > 1) { | ||||||
|  |                                 self.timeout.setData(self.timeout.data - 1); | ||||||
|  |                                 window.setTimeout( | ||||||
|  |                                     countDown, | ||||||
|  |                                     1000 | ||||||
|                                 ) |                                 ) | ||||||
|  |                             } else { | ||||||
|  |                                 self.timeout.setData(0); | ||||||
|  |                                 self.update() | ||||||
|  |                             } | ||||||
|  |                         }, 1000  | ||||||
|  |                     ) | ||||||
|  |                 } | ||||||
|  |                 countDown(); | ||||||
|  | 
 | ||||||
|             } |             } | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|          |  | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     private IsInBounds(bounds: Bounds): boolean { |     private IsInBounds(bounds: Bounds): boolean { | ||||||
|         if (this._previousBounds === undefined) { |         if (this._previousBounds === undefined) { | ||||||
|             return false; |             return false; | ||||||
|  | @ -173,6 +189,4 @@ export default class UpdateFromOverpass implements FeatureSource{ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|      |  | ||||||
|      |  | ||||||
| } | } | ||||||
|  | @ -12,7 +12,7 @@ export default class FeatureDuplicatorPerLayer implements FeatureSource { | ||||||
|     public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; |     public readonly features: UIEventSource<{ feature: any; freshness: Date }[]>; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     constructor(layers: { layerDef: LayerConfig }[], upstream: FeatureSource) { |     constructor(layers: UIEventSource<{ layerDef: LayerConfig }[]>, upstream: FeatureSource) { | ||||||
|         this.features = upstream.features.map(features => { |         this.features = upstream.features.map(features => { | ||||||
|             const newFeatures: { feature: any, freshness: Date }[] = []; |             const newFeatures: { feature: any, freshness: Date }[] = []; | ||||||
|             if(features === undefined){ |             if(features === undefined){ | ||||||
|  | @ -29,7 +29,7 @@ export default class FeatureDuplicatorPerLayer implements FeatureSource { | ||||||
| 
 | 
 | ||||||
|                  |                  | ||||||
|                 let foundALayer = false; |                 let foundALayer = false; | ||||||
|                 for (const layer of layers) { |                 for (const layer of layers.data) { | ||||||
|                     if (layer.layerDef.overpassTags.matchesProperties(f.feature.properties)) { |                     if (layer.layerDef.overpassTags.matchesProperties(f.feature.properties)) { | ||||||
|                         foundALayer = true; |                         foundALayer = true; | ||||||
|                         if (layer.layerDef.passAllFeatures) { |                         if (layer.layerDef.passAllFeatures) { | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ export default class FeaturePipeline implements FeatureSource { | ||||||
| 
 | 
 | ||||||
|     public features: UIEventSource<{ feature: any; freshness: Date }[]>; |     public features: UIEventSource<{ feature: any; freshness: Date }[]>; | ||||||
| 
 | 
 | ||||||
|     constructor(flayers: { isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }[], |     constructor(flayers: UIEventSource<{ isDisplayed: UIEventSource<boolean>, layerDef: LayerConfig }[]>, | ||||||
|                 updater: FeatureSource, |                 updater: FeatureSource, | ||||||
|                 layout: UIEventSource<LayoutConfig>, |                 layout: UIEventSource<LayoutConfig>, | ||||||
|                 newPoints: FeatureSource, |                 newPoints: FeatureSource, | ||||||
|  |  | ||||||
|  | @ -6,23 +6,24 @@ import Loc from "../../Models/Loc"; | ||||||
| export default class FilteringFeatureSource implements FeatureSource { | export default class FilteringFeatureSource implements FeatureSource { | ||||||
|     public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); |     public features: UIEventSource<{ feature: any; freshness: Date }[]> = new UIEventSource<{ feature: any; freshness: Date }[]>([]); | ||||||
| 
 | 
 | ||||||
|     constructor(layers: { |     constructor(layers: UIEventSource<{ | ||||||
|                     isDisplayed: UIEventSource<boolean>, |                     isDisplayed: UIEventSource<boolean>, | ||||||
|                     layerDef: LayerConfig |                     layerDef: LayerConfig | ||||||
|                 }[], |                 }[]>, | ||||||
|                 location: UIEventSource<Loc>, |                 location: UIEventSource<Loc>, | ||||||
|                 upstream: FeatureSource) { |                 upstream: FeatureSource) { | ||||||
| 
 | 
 | ||||||
|         const self = this; |         const self = this; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  |         function update() { | ||||||
|  | 
 | ||||||
|             const layerDict = {}; |             const layerDict = {}; | ||||||
|         for (const layer of layers) { |             for (const layer of layers.data) { | ||||||
|                 layerDict[layer.layerDef.id] = layer; |                 layerDict[layer.layerDef.id] = layer; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         |             console.log("Updating the filtering layer, input ", upstream.features.data.length, "features") | ||||||
|         function update() { |  | ||||||
|             console.log("Updating the filtering layer") |  | ||||||
|             const features: { feature: any, freshness: Date }[] = upstream.features.data; |             const features: { feature: any, freshness: Date }[] = upstream.features.data; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -42,7 +43,7 @@ export default class FilteringFeatureSource implements FeatureSource { | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 // Does it match any other layer - e.g. because of a switch?
 |                 // Does it match any other layer - e.g. because of a switch?
 | ||||||
|                 for (const toCheck of layers) { |                 for (const toCheck of layers.data) { | ||||||
|                     if (!FilteringFeatureSource.showLayer(toCheck, location)) { |                     if (!FilteringFeatureSource.showLayer(toCheck, location)) { | ||||||
|                         continue; |                         continue; | ||||||
|                     } |                     } | ||||||
|  | @ -53,6 +54,8 @@ export default class FilteringFeatureSource implements FeatureSource { | ||||||
|                 return false; |                 return false; | ||||||
| 
 | 
 | ||||||
|             }); |             }); | ||||||
|  |             console.log("Updating the filtering layer, output ", newFeatures.length, "features") | ||||||
|  | 
 | ||||||
|             self.features.setData(newFeatures); |             self.features.setData(newFeatures); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -63,21 +66,34 @@ export default class FilteringFeatureSource implements FeatureSource { | ||||||
|         location.map(l => { |         location.map(l => { | ||||||
|             // We want something that is stable for the shown layers
 |             // We want something that is stable for the shown layers
 | ||||||
|             const displayedLayerIndexes = []; |             const displayedLayerIndexes = []; | ||||||
|             for (let i = 0; i < layers.length; i++) { |             for (let i = 0; i < layers.data.length; i++) { | ||||||
|                 if (l.zoom < layers[i].layerDef.minzoom) { |                 const layer = layers.data[i]; | ||||||
|  |                 if (l.zoom < layer.layerDef.minzoom) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|                 if (!layers[i].isDisplayed.data) { |                 if (!layer.isDisplayed.data) { | ||||||
|                     continue; |                     continue; | ||||||
|                 } |                 } | ||||||
|                 displayedLayerIndexes.push(i); |                 displayedLayerIndexes.push(i); | ||||||
|             } |             } | ||||||
|             return displayedLayerIndexes.join(",") |             return displayedLayerIndexes.join(",") | ||||||
|         }, layers.map(l => l.isDisplayed)) |         }).addCallback(() => { | ||||||
|             .addCallback(() => { |  | ||||||
|             update(); |             update(); | ||||||
|         }); |         }); | ||||||
|          |          | ||||||
|  |         layers.addCallback(update); | ||||||
|  |          | ||||||
|  |         const registered = new Set<UIEventSource<boolean>>(); | ||||||
|  |         layers.addCallback(layers => { | ||||||
|  |             for (const layer of layers) { | ||||||
|  |                 if(registered.has(layer.isDisplayed)){ | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 registered.add(layer.isDisplayed); | ||||||
|  |                 layer.isDisplayed.addCallback(update); | ||||||
|  |             } | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|         update(); |         update(); | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -9,6 +9,9 @@ export default class LocalStorageSource implements FeatureSource { | ||||||
|     constructor(layout: UIEventSource<LayoutConfig>) { |     constructor(layout: UIEventSource<LayoutConfig>) { | ||||||
|         this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) |         this.features = new UIEventSource<{ feature: any; freshness: Date }[]>([]) | ||||||
|         const key = LocalStorageSaver.storageKey + layout.data.id |         const key = LocalStorageSaver.storageKey + layout.data.id | ||||||
|  |         layout.addCallbackAndRun(_ => { | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|             try { |             try { | ||||||
|                 const fromStorage = localStorage.getItem(key); |                 const fromStorage = localStorage.getItem(key); | ||||||
|                 if (fromStorage == null) { |                 if (fromStorage == null) { | ||||||
|  | @ -16,11 +19,11 @@ export default class LocalStorageSource implements FeatureSource { | ||||||
|                 } |                 } | ||||||
|                 const loaded = JSON.parse(fromStorage); |                 const loaded = JSON.parse(fromStorage); | ||||||
|                 this.features.setData(loaded); |                 this.features.setData(loaded); | ||||||
|             console.log("Loaded ",loaded.length," features from localstorage as cache") |                 console.log("Loaded ", loaded.length, " features from localstorage as cache") | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|                 console.log("Could not load features from localStorage:", e) |                 console.log("Could not load features from localStorage:", e) | ||||||
|                 localStorage.removeItem(key) |                 localStorage.removeItem(key) | ||||||
|             } |             } | ||||||
| 
 |         }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | @ -12,16 +12,12 @@ export default class NoOverlapSource { | ||||||
| 
 | 
 | ||||||
|     features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<{ feature: any, freshness: Date }[]>([]); |     features: UIEventSource<{ feature: any, freshness: Date }[]> = new UIEventSource<{ feature: any, freshness: Date }[]>([]); | ||||||
| 
 | 
 | ||||||
|     constructor(layers: { |     constructor(layers: UIEventSource<{ | ||||||
|                     layerDef: LayerConfig |                     layerDef: LayerConfig | ||||||
|                 }[], |                 }[]>, | ||||||
|                 upstream: FeatureSource) { |                 upstream: FeatureSource) { | ||||||
|         const layerDict = {}; |  | ||||||
|         let noOverlapRemoval = true; |         let noOverlapRemoval = true; | ||||||
|         const layerIds = [] |         for (const layer of layers.data) { | ||||||
|         for (const layer of layers) { |  | ||||||
|             layerDict[layer.layerDef.id] = layer; |  | ||||||
|             layerIds.push(layer.layerDef.id); |  | ||||||
|             if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) { |             if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) { | ||||||
|                 noOverlapRemoval = false; |                 noOverlapRemoval = false; | ||||||
|             } |             } | ||||||
|  | @ -31,13 +27,22 @@ export default class NoOverlapSource { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         this.features = upstream.features.map( |         this.features = upstream.features.map( | ||||||
|             features => { |             features => { | ||||||
|                 if (features === undefined) { |                 if (features === undefined) { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  |                 const layerIds = [] | ||||||
|  |                 const layerDict = {}; | ||||||
|  |                 for (const layer of layers.data) { | ||||||
|  |                     layerDict[layer.layerDef.id] = layer; | ||||||
|  |                     layerIds.push(layer.layerDef.id); | ||||||
|  |                     if ((layer.layerDef.hideUnderlayingFeaturesMinPercentage ?? 0) !== 0) { | ||||||
|  |                         noOverlapRemoval = false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|                 // There is overlap removal active
 |                 // There is overlap removal active
 | ||||||
|                 // We partition all the features with their respective layerIDs
 |                 // We partition all the features with their respective layerIDs
 | ||||||
|                 const partitions = {}; |                 const partitions = {}; | ||||||
|  | @ -67,7 +72,7 @@ export default class NoOverlapSource { | ||||||
|                                 guardPartition.map(f => f.feature), |                                 guardPartition.map(f => f.feature), | ||||||
|                                 percentage |                                 percentage | ||||||
|                             ); |                             ); | ||||||
|                             if(!doesOverlap){ |                             if (!doesOverlap) { | ||||||
|                                 newPartition.push(mightBeDeleted); |                                 newPartition.push(mightBeDeleted); | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|  |  | ||||||
|  | @ -6,29 +6,26 @@ import {GeoOperations} from "../GeoOperations"; | ||||||
| export default class WayHandlingApplyingFeatureSource implements FeatureSource { | export default class WayHandlingApplyingFeatureSource implements FeatureSource { | ||||||
|     features: UIEventSource<{ feature: any; freshness: Date }[]>; |     features: UIEventSource<{ feature: any; freshness: Date }[]>; | ||||||
| 
 | 
 | ||||||
|     constructor(layers: { |     constructor(layers: UIEventSource<{ | ||||||
|                     layerDef: LayerConfig |                     layerDef: LayerConfig | ||||||
|                 }[], |                 }[]>, | ||||||
|                 upstream: FeatureSource) { |                 upstream: FeatureSource) { | ||||||
|         const layerDict = {}; |  | ||||||
|         let allDefaultWayHandling = true; |  | ||||||
|         for (const layer of layers) { |  | ||||||
|             layerDict[layer.layerDef.id] = layer; |  | ||||||
|             if (layer.layerDef.wayHandling !== LayerConfig.WAYHANDLING_DEFAULT) { |  | ||||||
|                 allDefaultWayHandling = false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if (allDefaultWayHandling) { |  | ||||||
|             this.features = upstream.features; |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|         this.features = upstream.features.map( |         this.features = upstream.features.map( | ||||||
|             features => { |             features => { | ||||||
|                 if(features === undefined){ |                 if(features === undefined){ | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  | 
 | ||||||
|  |                 const layerDict = {}; | ||||||
|  |                 let allDefaultWayHandling = true; | ||||||
|  |                 for (const layer of layers.data) { | ||||||
|  |                     layerDict[layer.layerDef.id] = layer; | ||||||
|  |                     if (layer.layerDef.wayHandling !== LayerConfig.WAYHANDLING_DEFAULT) { | ||||||
|  |                         allDefaultWayHandling = false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                  | ||||||
|                 const newFeatures: { feature: any, freshness: Date }[] = []; |                 const newFeatures: { feature: any, freshness: Date }[] = []; | ||||||
|                 for (const f of features) { |                 for (const f of features) { | ||||||
|                     const feat = f.feature; |                     const feat = f.feature; | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								State.ts
									
										
									
									
									
								
							|  | @ -123,7 +123,8 @@ export default class State { | ||||||
|         this.layoutToUse.setData(layoutToUse); |         this.layoutToUse.setData(layoutToUse); | ||||||
| 
 | 
 | ||||||
|         // -- Location control initialization
 |         // -- Location control initialization
 | ||||||
|         {    const zoom = State.asFloat( |         { | ||||||
|  |             const zoom = State.asFloat( | ||||||
|                 QueryParameters.GetQueryParameter("z", "" + (layoutToUse?.startZoom ?? 1), "The initial/current zoom level") |                 QueryParameters.GetQueryParameter("z", "" + (layoutToUse?.startZoom ?? 1), "The initial/current zoom level") | ||||||
|                     .syncWith(LocalStorageSource.Get("zoom"))); |                     .syncWith(LocalStorageSource.Get("zoom"))); | ||||||
|             const lat = State.asFloat(QueryParameters.GetQueryParameter("lat", "" + (layoutToUse?.startLat ?? 0), "The initial/current latitude") |             const lat = State.asFloat(QueryParameters.GetQueryParameter("lat", "" + (layoutToUse?.startLat ?? 0), "The initial/current latitude") | ||||||
|  | @ -151,6 +152,7 @@ export default class State { | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|         // Helper function to initialize feature switches
 |         // Helper function to initialize feature switches
 | ||||||
|         function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource<boolean> { |         function featSw(key: string, deflt: (layout: LayoutConfig) => boolean, documentation: string): UIEventSource<boolean> { | ||||||
|             const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation); |             const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined, documentation); | ||||||
|  | @ -190,7 +192,7 @@ export default class State { | ||||||
| 
 | 
 | ||||||
|             this.featureSwitchIsTesting = QueryParameters.GetQueryParameter("test", "false", |             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") |                 "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); |                 .map(str => str === "true", [], b => "" + b); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -211,11 +213,12 @@ export default class State { | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         this.installedThemes = new InstalledThemes(this.osmConnection).installedThemes; |         this.installedThemes = new InstalledThemes(this.osmConnection).installedThemes; | ||||||
| 
 | 
 | ||||||
|         // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
 |         // Important: the favourite layers are initialized _after_ the installed themes, as these might contain an installedTheme
 | ||||||
|         this.favouriteLayers = this.osmConnection.GetLongPreference("favouriteLayers").map( |         this.favouriteLayers = LocalStorageSource.Get("favouriteLayers") | ||||||
|  |             .syncWith(this.osmConnection.GetLongPreference("favouriteLayers")) | ||||||
|  |             .map( | ||||||
|                 str => Utils.Dedup(str?.split(";")) ?? [], |                 str => Utils.Dedup(str?.split(";")) ?? [], | ||||||
|                 [], layers => Utils.Dedup(layers)?.join(";") |                 [], layers => Utils.Dedup(layers)?.join(";") | ||||||
|             ); |             ); | ||||||
|  |  | ||||||
|  | @ -21,7 +21,6 @@ export default class ScrollableFullScreen extends UIElement { | ||||||
|                 Svg.close_svg().SetClass("hidden md:block") |                 Svg.close_svg().SetClass("hidden md:block") | ||||||
|             ]) |             ]) | ||||||
|                 .onClick(() => { |                 .onClick(() => { | ||||||
|                     console.log("Closing...") |  | ||||||
|                     ScrollableFullScreen.RestoreLeaflet(); |                     ScrollableFullScreen.RestoreLeaflet(); | ||||||
|                     if (onClose !== undefined) { |                     if (onClose !== undefined) { | ||||||
|                         onClose(); |                         onClose(); | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ export default class LayerControlPanel extends UIElement { | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (State.state.filteredLayers.data.length > 1) { |         if (State.state.filteredLayers.data.length > 1) { | ||||||
|             const layerSelection = new LayerSelection(); |             const layerSelection = new LayerSelection(State.state.filteredLayers); | ||||||
|             layerSelection.onClick(() => { |             layerSelection.onClick(() => { | ||||||
|             }); |             }); | ||||||
|             layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]); |             layerControlPanel = new Combine([layerSelection, "<br/>", layerControlPanel]); | ||||||
|  |  | ||||||
|  | @ -6,19 +6,36 @@ import CheckBox from "../Input/CheckBox"; | ||||||
| import Combine from "../Base/Combine"; | import Combine from "../Base/Combine"; | ||||||
| import {FixedUiElement} from "../Base/FixedUiElement"; | import {FixedUiElement} from "../Base/FixedUiElement"; | ||||||
| import Translations from "../i18n/Translations"; | import Translations from "../i18n/Translations"; | ||||||
|  | import LayerConfig from "../../Customizations/JSON/LayerConfig"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Shows the panel with all layers and a toggle for each of them |  * Shows the panel with all layers and a toggle for each of them | ||||||
|  */ |  */ | ||||||
| export default class LayerSelection extends UIElement { | export default class LayerSelection extends UIElement { | ||||||
| 
 | 
 | ||||||
|     private readonly _checkboxes: UIElement[]; |     private _checkboxes: UIElement[]; | ||||||
|  |     private activeLayers: UIEventSource<{ | ||||||
|  |         readonly isDisplayed: UIEventSource<boolean>, | ||||||
|  |         readonly layerDef: LayerConfig; | ||||||
|  |     }[]>; | ||||||
|  | 
 | ||||||
|  |     constructor(activeLayers: UIEventSource<{ | ||||||
|  |         readonly isDisplayed: UIEventSource<boolean>, | ||||||
|  |         readonly layerDef: LayerConfig; | ||||||
|  |     }[]>) { | ||||||
|  |         super(activeLayers); | ||||||
|  |         if(activeLayers === undefined){ | ||||||
|  |             throw "ActiveLayers should be defined..." | ||||||
|  |         } | ||||||
|  |         this.activeLayers = activeLayers; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     InnerRender(): string { | ||||||
| 
 | 
 | ||||||
|     constructor() { |  | ||||||
|         super(undefined); |  | ||||||
|         this._checkboxes = []; |         this._checkboxes = []; | ||||||
| 
 | 
 | ||||||
|         for (const layer of State.state.filteredLayers.data) { |         for (const layer of this.activeLayers.data) { | ||||||
|             const leafletStyle = layer.layerDef.GenerateLeafletStyle( |             const leafletStyle = layer.layerDef.GenerateLeafletStyle( | ||||||
|                 new UIEventSource<any>({id: "node/-1"}), |                 new UIEventSource<any>({id: "node/-1"}), | ||||||
|                 false) |                 false) | ||||||
|  | @ -54,9 +71,8 @@ export default class LayerSelection extends UIElement { | ||||||
|                 .SetStyle("margin:0.3em;") |                 .SetStyle("margin:0.3em;") | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     InnerRender(): string { | 
 | ||||||
|         return new Combine(this._checkboxes) |         return new Combine(this._checkboxes) | ||||||
|             .SetStyle("display:flex;flex-direction:column;") |             .SetStyle("display:flex;flex-direction:column;") | ||||||
|             .Render(); |             .Render(); | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ export default class CenterMessageBox extends UIElement { | ||||||
|         super(State.state.centerMessage); |         super(State.state.centerMessage); | ||||||
| 
 | 
 | ||||||
|         this.ListenTo(State.state.locationControl); |         this.ListenTo(State.state.locationControl); | ||||||
|         this.ListenTo(State.state.layerUpdater.retries); |         this.ListenTo(State.state.layerUpdater.timeout); | ||||||
|         this.ListenTo(State.state.layerUpdater.runningQuery); |         this.ListenTo(State.state.layerUpdater.runningQuery); | ||||||
|         this.ListenTo(State.state.layerUpdater.sufficientlyZoomed); |         this.ListenTo(State.state.layerUpdater.sufficientlyZoomed); | ||||||
|     } |     } | ||||||
|  | @ -18,9 +18,9 @@ export default class CenterMessageBox extends UIElement { | ||||||
|             return {innerHtml: State.state.centerMessage.data, done: false}; |             return {innerHtml: State.state.centerMessage.data, done: false}; | ||||||
|         } |         } | ||||||
|         const lu = State.state.layerUpdater; |         const lu = State.state.layerUpdater; | ||||||
|         if (lu.retries.data > 0) { |         if (lu.timeout.data > 0) { | ||||||
|             return { |             return { | ||||||
|                 innerHtml: Translations.t.centerMessage.retrying.Subs({count: "" + lu.retries.data}).Render(), |                 innerHtml: Translations.t.centerMessage.retrying.Subs({count: "" + lu.timeout.data}).Render(), | ||||||
|                 done: false |                 done: false | ||||||
|             }; |             }; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -102,6 +102,10 @@ export default class TagRenderingQuestion extends UIElement { | ||||||
|             return ff; |             return ff; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if(ff){ | ||||||
|  |             mappings.push(ff); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if (this._configuration.multiAnswer) { |         if (this._configuration.multiAnswer) { | ||||||
|             return this.GenerateMultiAnswer(mappings, ff) |             return this.GenerateMultiAnswer(mappings, ff) | ||||||
|         } else { |         } else { | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ import {GeoOperations} from "../Logic/GeoOperations"; | ||||||
| 
 | 
 | ||||||
| export default class ShowDataLayer { | export default class ShowDataLayer { | ||||||
| 
 | 
 | ||||||
|     private readonly _layerDict; |     private _layerDict; | ||||||
|     private readonly _leafletMap: UIEventSource<L.Map>; |     private readonly _leafletMap: UIEventSource<L.Map>; | ||||||
| 
 | 
 | ||||||
|     constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>, |     constructor(features: UIEventSource<{ feature: any, freshness: Date }[]>, | ||||||
|  | @ -24,12 +24,11 @@ export default class ShowDataLayer { | ||||||
|         this._leafletMap = leafletMap; |         this._leafletMap = leafletMap; | ||||||
|         const self = this; |         const self = this; | ||||||
|         const mp = leafletMap.data; |         const mp = leafletMap.data; | ||||||
| 
 |         self._layerDict = {}; | ||||||
|         this._layerDict = {}; |  | ||||||
| 
 | 
 | ||||||
|         layoutToUse.addCallbackAndRun(layoutToUse => { |         layoutToUse.addCallbackAndRun(layoutToUse => { | ||||||
|             for (const layer of layoutToUse.layers) { |             for (const layer of layoutToUse.layers) { | ||||||
|                 this._layerDict[layer.id] = layer; |                 self._layerDict[layer.id] = layer; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|  | @ -81,7 +80,7 @@ export default class ShowDataLayer { | ||||||
|         const tagsSource = State.state.allElements.getEventSourceFor(feature); |         const tagsSource = State.state.allElements.getEventSourceFor(feature); | ||||||
|         // Every object is tied to exactly one layer
 |         // Every object is tied to exactly one layer
 | ||||||
|         const layer = this._layerDict[feature._matching_layer_id]; |         const layer = this._layerDict[feature._matching_layer_id]; | ||||||
|         return layer.GenerateLeafletStyle(tagsSource, layer._showOnPopup !== undefined); |         return layer?.GenerateLeafletStyle(tagsSource, layer._showOnPopup !== undefined); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private pointToLayer(feature, latLng): L.Layer { |     private pointToLayer(feature, latLng): L.Layer { | ||||||
|  | @ -111,6 +110,10 @@ export default class ShowDataLayer { | ||||||
| 
 | 
 | ||||||
|     private postProcessFeature(feature, leafletLayer: L.Layer) { |     private postProcessFeature(feature, leafletLayer: L.Layer) { | ||||||
|         const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; |         const layer: LayerConfig = this._layerDict[feature._matching_layer_id]; | ||||||
|  |         if(layer === undefined){ | ||||||
|  |             console.warn("No layer found for object (probably a now disabled layer)", feature) | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|         if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) { |         if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) { | ||||||
|             // No popup action defined -> Don't do anything
 |             // No popup action defined -> Don't do anything
 | ||||||
|             return; |             return; | ||||||
|  | @ -159,10 +162,10 @@ export default class ShowDataLayer { | ||||||
| 
 | 
 | ||||||
|                     const mp = this._leafletMap.data; |                     const mp = this._leafletMap.data; | ||||||
|                     if (!popup.isOpen() && mp !== undefined) { |                     if (!popup.isOpen() && mp !== undefined) { | ||||||
|                         var centerpoint = GeoOperations.centerpointCoordinates(feature); |  | ||||||
|                         popup |                         popup | ||||||
|                             .setLatLng(GeoOperations.centerpointCoordinates(feature)) |                             .setLatLng(GeoOperations.centerpointCoordinates(feature)) | ||||||
|                             .openOn(mp); |                             .openOn(mp); | ||||||
|  |                         uiElement.Activate(); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -1,6 +1,4 @@ | ||||||
| import * as $ from "jquery" | import * as $ from "jquery" | ||||||
| import Constants from "./Models/Constants"; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| export class Utils { | export class Utils { | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", |     "increase-memory": "export NODE_OPTIONS=--max_old_space_size=4096", | ||||||
|     "start": "npm run increase-memory && parcel *.html UI/** Logic/** assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", |     "start": "npm run increase-memory && parcel *.html UI/** Logic/** assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*", | ||||||
|     "test": "ts-node test/*", |     "test": "ts-node test/Tag.spec.ts && ts-node test/TagQuestion.spec.ts", | ||||||
|     "generate:editor-layer-index": "cd assets/ && wget https://osmlab.github.io/editor-layer-index/imagery.geojson --output-document=editor-layer-index.json", |     "generate:editor-layer-index": "cd assets/ && wget https://osmlab.github.io/editor-layer-index/imagery.geojson --output-document=editor-layer-index.json", | ||||||
|     "generate:images": "ts-node scripts/generateIncludedImages.ts", |     "generate:images": "ts-node scripts/generateIncludedImages.ts", | ||||||
|     "generate:translations": "ts-node scripts/generateTranslations.ts", |     "generate:translations": "ts-node scripts/generateTranslations.ts", | ||||||
|  |  | ||||||
|  | @ -15,7 +15,7 @@ import PublicHolidayInput from "../UI/OpeningHours/PublicHolidayInput"; | ||||||
| import {SubstitutedTranslation} from "../UI/SubstitutedTranslation"; | import {SubstitutedTranslation} from "../UI/SubstitutedTranslation"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| new T([ | new T("Tags", [ | ||||||
|     ["Tag replacement works in translation", () => { |     ["Tag replacement works in translation", () => { | ||||||
|         const tr = new Translation({ |         const tr = new Translation({ | ||||||
|             "en": "Test {key} abc" |             "en": "Test {key} abc" | ||||||
|  |  | ||||||
							
								
								
									
										60
									
								
								test/TagQuestion.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								test/TagQuestion.spec.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | ||||||
|  | import T from "./TestHelper"; | ||||||
|  | import {Utils} from "../Utils"; | ||||||
|  | 
 | ||||||
|  | Utils.runningFromConsole = true; | ||||||
|  | import TagRenderingQuestion from "../UI/Popup/TagRenderingQuestion"; | ||||||
|  | import {UIEventSource} from "../Logic/UIEventSource"; | ||||||
|  | import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; | ||||||
|  | import {equal} from "assert"; | ||||||
|  | import * as assert from "assert"; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | new T("TagQuestionElement", | ||||||
|  |     [ | ||||||
|  |         ["Freeform has textfield", () => { | ||||||
|  |             const tags = new UIEventSource({ | ||||||
|  |                 id: "way/123", | ||||||
|  |                 amenity: 'public_bookcases' | ||||||
|  |             }); | ||||||
|  |             const config = new TagRenderingConfig( | ||||||
|  |                 { | ||||||
|  |                     render: "The name is {name}", | ||||||
|  |                     question: "What is the name of this bookcase?", | ||||||
|  |                     freeform: { | ||||||
|  |                         key: "name", | ||||||
|  |                         type: "string" | ||||||
|  |                     } | ||||||
|  |                 }, undefined, "Testing tag" | ||||||
|  |             ); | ||||||
|  |             const questionElement = new TagRenderingQuestion(tags, config); | ||||||
|  |             const html = questionElement.InnerRender(); | ||||||
|  |             T.assertContains("What is the name of this bookcase?", html); | ||||||
|  |             T.assertContains("<input type='text'", html); | ||||||
|  |         }], | ||||||
|  |         ["TagsQuestion with Freeform and mappings has textfield", () => { | ||||||
|  |             const tags = new UIEventSource({ | ||||||
|  |                 id: "way/123", | ||||||
|  |                 amenity: 'public_bookcases' | ||||||
|  |             }); | ||||||
|  |             const config = new TagRenderingConfig( | ||||||
|  |                 { | ||||||
|  |                     render: "The name is {name}", | ||||||
|  |                     question: "What is the name of this bookcase?", | ||||||
|  |                     freeform: { | ||||||
|  |                         key: "name", | ||||||
|  |                         type: "string" | ||||||
|  |                     }, | ||||||
|  |                     mappings: [ | ||||||
|  |                         {"if": "noname=yes", | ||||||
|  |                         "then": "This bookcase has no name"} | ||||||
|  |                     ] | ||||||
|  |                 }, undefined, "Testing tag" | ||||||
|  |             ); | ||||||
|  |             const questionElement = new TagRenderingQuestion(tags, config); | ||||||
|  |             const html = questionElement.InnerRender(); | ||||||
|  |             T.assertContains("What is the name of this bookcase?", html); | ||||||
|  |             T.assertContains("This bookcase has no name", html); | ||||||
|  |             T.assertContains("<input type='text'", html); | ||||||
|  |         }] | ||||||
|  |     ] | ||||||
|  | ); | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
|  | 
 | ||||||
| export default class T { | export default class T { | ||||||
| 
 | 
 | ||||||
|     constructor(tests: [string, () => void ][]) { |     constructor(testsuite: string, tests: [string, () => void ][]) { | ||||||
|         let failures : string []= []; |         let failures : string []= []; | ||||||
|         for (const [name, test] of tests) { |         for (const [name, test] of tests) { | ||||||
|             try { |             try { | ||||||
|  | @ -11,11 +12,17 @@ export default class T { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         if (failures.length == 0) { |         if (failures.length == 0) { | ||||||
|             console.log("All tests done!") |             console.log(`All tests of ${testsuite} done!`) | ||||||
|         } else { |         } else { | ||||||
|             console.warn(failures.length, "tests failed :(") |             console.warn(failures.length, `tests of ${testsuite} failed :(`) | ||||||
|             console.log("Failed tests: ", failures.join(",")) |             console.log("Failed tests: ", failures.join(",")) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     static assertContains(needle: string, actual: string){ | ||||||
|  |         if(actual.indexOf(needle) < 0){ | ||||||
|  |             throw `The substring ${needle} was not found` | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue