Stabilize personal theme, textfield now correctly appears if it is an option in the freeform too

This commit is contained in:
pietervdvn 2021-02-20 01:45:51 +01:00
parent 79fc3f54e5
commit 416a76ae4f
22 changed files with 278 additions and 149 deletions

View file

@ -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 }[]) => {

View file

@ -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]);

View file

@ -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;
} }

View file

@ -17,8 +17,8 @@ export default class UpdateFromOverpass implements FeatureSource{
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
* *
@ -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()
}); });
} }
@ -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{
} }
} }

View file

@ -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) {

View file

@ -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,

View file

@ -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();
} }

View file

@ -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) {
@ -21,6 +24,6 @@ export default class LocalStorageSource implements FeatureSource {
console.log("Could not load features from localStorage:", e) console.log("Could not load features from localStorage:", e)
localStorage.removeItem(key) localStorage.removeItem(key)
} }
})
} }
} }

View file

@ -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 = {};

View file

@ -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;

View file

@ -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);
@ -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(";")
); );

View file

@ -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();

View file

@ -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]);

View file

@ -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();

View file

@ -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
}; };
} }

View file

@ -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 {

View file

@ -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();
} }
} }
} }

View file

@ -1,6 +1,4 @@
import * as $ from "jquery" import * as $ from "jquery"
import Constants from "./Models/Constants";
export class Utils { export class Utils {

View file

@ -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",

View file

@ -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
View 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);
}]
]
);

View file

@ -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`
}
}
} }