forked from MapComplete/MapComplete
		
	Merge branch 'develop' into features/left-right-rendering
This commit is contained in:
		
						commit
						7b9836f273
					
				
					 43 changed files with 1324 additions and 1118 deletions
				
			
		| 
						 | 
					@ -23,11 +23,11 @@ export default class AvailableBaseLayers {
 | 
				
			||||||
    private static implementation: AvailableBaseLayersObj
 | 
					    private static implementation: AvailableBaseLayersObj
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    static AvailableLayersAt(location: UIEventSource<Loc>): UIEventSource<BaseLayer[]> {
 | 
					    static AvailableLayersAt(location: UIEventSource<Loc>): UIEventSource<BaseLayer[]> {
 | 
				
			||||||
        return AvailableBaseLayers.implementation.AvailableLayersAt(location);
 | 
					        return AvailableBaseLayers.implementation?.AvailableLayersAt(location) ?? new UIEventSource<BaseLayer[]>([]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer> {
 | 
					    static SelectBestLayerAccordingTo(location: UIEventSource<Loc>, preferedCategory: UIEventSource<string | string[]>): UIEventSource<BaseLayer> {
 | 
				
			||||||
        return AvailableBaseLayers.implementation.SelectBestLayerAccordingTo(location, preferedCategory);
 | 
					        return AvailableBaseLayers.implementation?.SelectBestLayerAccordingTo(location, preferedCategory) ?? new UIEventSource<BaseLayer>(undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,7 @@ import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
import BaseLayer from "../../Models/BaseLayer";
 | 
					import BaseLayer from "../../Models/BaseLayer";
 | 
				
			||||||
import AvailableBaseLayers from "./AvailableBaseLayers";
 | 
					import AvailableBaseLayers from "./AvailableBaseLayers";
 | 
				
			||||||
import Loc from "../../Models/Loc";
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Sets the current background layer to a layer that is actually available
 | 
					 * Sets the current background layer to a layer that is actually available
 | 
				
			||||||
| 
						 | 
					@ -12,6 +13,11 @@ export default class BackgroundLayerResetter {
 | 
				
			||||||
                location: UIEventSource<Loc>,
 | 
					                location: UIEventSource<Loc>,
 | 
				
			||||||
                availableLayers: UIEventSource<BaseLayer[]>,
 | 
					                availableLayers: UIEventSource<BaseLayer[]>,
 | 
				
			||||||
                defaultLayerId: string = undefined) {
 | 
					                defaultLayerId: string = undefined) {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if(Utils.runningFromConsole){
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        defaultLayerId = defaultLayerId ?? AvailableBaseLayers.osmCarto.id;
 | 
					        defaultLayerId = defaultLayerId ?? AvailableBaseLayers.osmCarto.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Change the baselayer back to OSM if we go out of the current range of the layer
 | 
					        // Change the baselayer back to OSM if we go out of the current range of the layer
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,7 @@ export default class OverpassFeatureSource implements FeatureSource {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    private readonly _isActive: UIEventSource<boolean>;
 | 
					    private readonly _isActive: UIEventSource<boolean>;
 | 
				
			||||||
    private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[], zoomlevel: number) => void;
 | 
					    private readonly onBboxLoaded: (bbox: BBox, date: Date, layers: LayerConfig[], zoomlevel: number) => void;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
        state: {
 | 
					        state: {
 | 
				
			||||||
            readonly locationControl: UIEventSource<Loc>,
 | 
					            readonly locationControl: UIEventSource<Loc>,
 | 
				
			||||||
| 
						 | 
					@ -90,7 +91,7 @@ export default class OverpassFeatureSource implements FeatureSource {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const self = this;
 | 
					        const self = this;
 | 
				
			||||||
        this.updateAsync(paddedZoomLevel).then(bboxDate => {
 | 
					        this.updateAsync(paddedZoomLevel).then(bboxDate => {
 | 
				
			||||||
            if(bboxDate === undefined || self.onBboxLoaded === undefined){
 | 
					            if (bboxDate === undefined || self.onBboxLoaded === undefined) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const [bbox, date, layers] = bboxDate
 | 
					            const [bbox, date, layers] = bboxDate
 | 
				
			||||||
| 
						 | 
					@ -109,12 +110,10 @@ export default class OverpassFeatureSource implements FeatureSource {
 | 
				
			||||||
            return undefined;
 | 
					            return undefined;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(padToZoomLevel);
 | 
					        let data: any = undefined
 | 
				
			||||||
 | 
					        let date: Date = undefined
 | 
				
			||||||
 | 
					        let lastUsed = 0;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (bounds === undefined) {
 | 
					 | 
				
			||||||
            return undefined;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const self = this;
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const layersToDownload = []
 | 
					        const layersToDownload = []
 | 
				
			||||||
| 
						 | 
					@ -123,7 +122,7 @@ export default class OverpassFeatureSource implements FeatureSource {
 | 
				
			||||||
            if (typeof (layer) === "string") {
 | 
					            if (typeof (layer) === "string") {
 | 
				
			||||||
                throw "A layer was not expanded!"
 | 
					                throw "A layer was not expanded!"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        if(this.state.locationControl.data.zoom < layer.minzoom){
 | 
					            if (this.state.locationControl.data.zoom < layer.minzoom) {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if (layer.doNotDownload) {
 | 
					            if (layer.doNotDownload) {
 | 
				
			||||||
| 
						 | 
					@ -136,14 +135,18 @@ export default class OverpassFeatureSource implements FeatureSource {
 | 
				
			||||||
            layersToDownload.push(layer)
 | 
					            layersToDownload.push(layer)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let data: any = undefined
 | 
					        const self = this;
 | 
				
			||||||
        let date: Date = undefined
 | 
					 | 
				
			||||||
        const overpassUrls = self.state.overpassUrl.data
 | 
					        const overpassUrls = self.state.overpassUrl.data
 | 
				
			||||||
        let lastUsed = 0;
 | 
					        let bounds : BBox 
 | 
				
			||||||
 | 
					 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                bounds = this.state.currentBounds.data?.pad(this.state.layoutToUse.widenFactor)?.expandToTileBounds(padToZoomLevel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (bounds === undefined) {
 | 
				
			||||||
 | 
					                    return undefined;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const overpass = this.GetFilter(overpassUrls[lastUsed], layersToDownload);
 | 
					                const overpass = this.GetFilter(overpassUrls[lastUsed], layersToDownload);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (overpass === undefined) {
 | 
					                if (overpass === undefined) {
 | 
				
			||||||
| 
						 | 
					@ -175,7 +178,7 @@ export default class OverpassFeatureSource implements FeatureSource {
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } while (data === undefined);
 | 
					        } while (data === undefined && this._isActive.data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.retries.setData(0);
 | 
					        self.retries.setData(0);
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,7 @@
 | 
				
			||||||
import {Changes} from "../Osm/Changes";
 | 
					import {Changes} from "../Osm/Changes";
 | 
				
			||||||
import Constants from "../../Models/Constants";
 | 
					import Constants from "../../Models/Constants";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PendingChangesUploader {
 | 
					export default class PendingChangesUploader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +31,10 @@ export default class PendingChangesUploader {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if(Utils.runningFromConsole){
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        document.addEventListener('mouseout', e => {
 | 
					        document.addEventListener('mouseout', e => {
 | 
				
			||||||
            // @ts-ignore
 | 
					            // @ts-ignore
 | 
				
			||||||
            if (!e.toElement && !e.relatedTarget) {
 | 
					            if (!e.toElement && !e.relatedTarget) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,13 @@ import {OsmConnection} from "../Osm/OsmConnection";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class SelectedElementTagsUpdater {
 | 
					export default class SelectedElementTagsUpdater {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static readonly metatags = new Set(["timestamp",
 | 
				
			||||||
 | 
					        "version",
 | 
				
			||||||
 | 
					        "changeset",
 | 
				
			||||||
 | 
					        "user",
 | 
				
			||||||
 | 
					        "uid",
 | 
				
			||||||
 | 
					        "id"]                                         )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(state: {
 | 
					    constructor(state: {
 | 
				
			||||||
        selectedElement: UIEventSource<any>,
 | 
					        selectedElement: UIEventSource<any>,
 | 
				
			||||||
        allElements: ElementStorage,
 | 
					        allElements: ElementStorage,
 | 
				
			||||||
| 
						 | 
					@ -18,7 +25,7 @@ export default class SelectedElementTagsUpdater {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        state.osmConnection.isLoggedIn.addCallbackAndRun(isLoggedIn => {
 | 
					        state.osmConnection.isLoggedIn.addCallbackAndRun(isLoggedIn => {
 | 
				
			||||||
            if(isLoggedIn){
 | 
					            if (isLoggedIn) {
 | 
				
			||||||
                SelectedElementTagsUpdater.installCallback(state)
 | 
					                SelectedElementTagsUpdater.installCallback(state)
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -26,7 +33,7 @@ export default class SelectedElementTagsUpdater {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static installCallback(state: {
 | 
					    public static installCallback(state: {
 | 
				
			||||||
        selectedElement: UIEventSource<any>,
 | 
					        selectedElement: UIEventSource<any>,
 | 
				
			||||||
        allElements: ElementStorage,
 | 
					        allElements: ElementStorage,
 | 
				
			||||||
        changes: Changes,
 | 
					        changes: Changes,
 | 
				
			||||||
| 
						 | 
					@ -38,38 +45,38 @@ export default class SelectedElementTagsUpdater {
 | 
				
			||||||
            let id = s.properties?.id
 | 
					            let id = s.properties?.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const backendUrl = state.osmConnection._oauth_config.url
 | 
					            const backendUrl = state.osmConnection._oauth_config.url
 | 
				
			||||||
            if(id.startsWith(backendUrl)){
 | 
					            if (id.startsWith(backendUrl)) {
 | 
				
			||||||
                id = id.substring(backendUrl.length)
 | 
					                id = id.substring(backendUrl.length)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(!(id.startsWith("way") || id.startsWith("node") || id.startsWith("relation"))){
 | 
					            if (!(id.startsWith("way") || id.startsWith("node") || id.startsWith("relation"))) {
 | 
				
			||||||
                // This object is _not_ from OSM, so we skip it!
 | 
					                // This object is _not_ from OSM, so we skip it!
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(id.indexOf("-") >= 0){
 | 
					            if (id.indexOf("-") >= 0) {
 | 
				
			||||||
                // This is a new object
 | 
					                // This is a new object
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            OsmObject.DownloadPropertiesOf(id).then(latestTags => {
 | 
				
			||||||
            OsmObject.DownloadPropertiesOf(id).then(tags => {
 | 
					                SelectedElementTagsUpdater.applyUpdate(state, latestTags, id)
 | 
				
			||||||
                SelectedElementTagsUpdater.applyUpdate(state, tags, id)
 | 
					 | 
				
			||||||
            }).catch(e => {
 | 
					 | 
				
			||||||
                console.error("Could not update tags of ", id, "due to", e)
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static applyUpdate(state: {
 | 
					    public static applyUpdate(state: {
 | 
				
			||||||
                                  selectedElement: UIEventSource<any>,
 | 
					                                  selectedElement: UIEventSource<any>,
 | 
				
			||||||
                                  allElements: ElementStorage,
 | 
					                                  allElements: ElementStorage,
 | 
				
			||||||
                                  changes: Changes,
 | 
					                                  changes: Changes,
 | 
				
			||||||
                                  osmConnection: OsmConnection
 | 
					                                  osmConnection: OsmConnection
 | 
				
			||||||
                              }, latestTags: any, id: string
 | 
					                              }, latestTags: any, id: string
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const pendingChanges = state.changes.pendingChanges.data
 | 
					            const pendingChanges = state.changes.pendingChanges.data
 | 
				
			||||||
            .filter(change => change.type +"/"+ change.id === id)
 | 
					                .filter(change => change.type + "/" + change.id === id)
 | 
				
			||||||
                .filter(change => change.tags !== undefined);
 | 
					                .filter(change => change.tags !== undefined);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            for (const pendingChange of pendingChanges) {
 | 
					            for (const pendingChange of pendingChanges) {
 | 
				
			||||||
| 
						 | 
					@ -92,24 +99,43 @@ export default class SelectedElementTagsUpdater {
 | 
				
			||||||
            for (const key in latestTags) {
 | 
					            for (const key in latestTags) {
 | 
				
			||||||
                let osmValue = latestTags[key]
 | 
					                let osmValue = latestTags[key]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if(typeof osmValue === "number"){
 | 
					                if (typeof osmValue === "number") {
 | 
				
			||||||
                osmValue = ""+osmValue
 | 
					                    osmValue = "" + osmValue
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const localValue = currentTags[key]
 | 
					                const localValue = currentTags[key]
 | 
				
			||||||
                if (localValue !== osmValue) {
 | 
					                if (localValue !== osmValue) {
 | 
				
			||||||
                console.log("Local value for ", key ,":", localValue, "upstream", osmValue)
 | 
					                    console.log("Local value for ", key, ":", localValue, "upstream", osmValue)
 | 
				
			||||||
                    somethingChanged = true;
 | 
					                    somethingChanged = true;
 | 
				
			||||||
                    currentTags[key] = osmValue
 | 
					                    currentTags[key] = osmValue
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const currentKey in currentTags) {
 | 
				
			||||||
 | 
					                if (currentKey.startsWith("_")) {
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if(this.metatags.has(currentKey)){
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (currentKey in latestTags) {
 | 
				
			||||||
 | 
					                    continue
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                console.log("Removing key as deleted upstream", currentKey)
 | 
				
			||||||
 | 
					                delete currentTags[currentKey]
 | 
				
			||||||
 | 
					                somethingChanged = true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (somethingChanged) {
 | 
					            if (somethingChanged) {
 | 
				
			||||||
                console.log("Detected upstream changes to the object when opening it, updating...")
 | 
					                console.log("Detected upstream changes to the object when opening it, updating...")
 | 
				
			||||||
                currentTagsSource.ping()
 | 
					                currentTagsSource.ping()
 | 
				
			||||||
        }else{
 | 
					            } else {
 | 
				
			||||||
                console.debug("Fetched latest tags for ", id, "but detected no changes")
 | 
					                console.debug("Fetched latest tags for ", id, "but detected no changes")
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (e) {
 | 
				
			||||||
 | 
					            console.error("Updating the tags of selected element ", id, "failed due to", e)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,15 +3,20 @@ import {OsmObject} from "../Osm/OsmObject";
 | 
				
			||||||
import Loc from "../../Models/Loc";
 | 
					import Loc from "../../Models/Loc";
 | 
				
			||||||
import {ElementStorage} from "../ElementStorage";
 | 
					import {ElementStorage} from "../ElementStorage";
 | 
				
			||||||
import FeaturePipeline from "../FeatureSource/FeaturePipeline";
 | 
					import FeaturePipeline from "../FeatureSource/FeaturePipeline";
 | 
				
			||||||
 | 
					import {GeoOperations} from "../GeoOperations";
 | 
				
			||||||
 | 
					import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Makes sure the hash shows the selected element and vice-versa.
 | 
					 * Makes sure the hash shows the selected element and vice-versa.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
export default class SelectedFeatureHandler {
 | 
					export default class SelectedFeatureHandler {
 | 
				
			||||||
    private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "", undefined])
 | 
					    private static readonly _no_trigger_on = new Set(["welcome", "copyright", "layers", "new", "", undefined])
 | 
				
			||||||
    hash: UIEventSource<string>;
 | 
					    private readonly hash: UIEventSource<string>;
 | 
				
			||||||
    private readonly state: {
 | 
					    private readonly state: {
 | 
				
			||||||
        selectedElement: UIEventSource<any>
 | 
					        selectedElement: UIEventSource<any>,
 | 
				
			||||||
 | 
					        allElements: ElementStorage,
 | 
				
			||||||
 | 
					        locationControl: UIEventSource<Loc>,
 | 
				
			||||||
 | 
					        layoutToUse: LayoutConfig
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(
 | 
					    constructor(
 | 
				
			||||||
| 
						 | 
					@ -19,7 +24,9 @@ export default class SelectedFeatureHandler {
 | 
				
			||||||
        state: {
 | 
					        state: {
 | 
				
			||||||
            selectedElement: UIEventSource<any>,
 | 
					            selectedElement: UIEventSource<any>,
 | 
				
			||||||
            allElements: ElementStorage,
 | 
					            allElements: ElementStorage,
 | 
				
			||||||
            featurePipeline: FeaturePipeline
 | 
					            featurePipeline: FeaturePipeline,
 | 
				
			||||||
 | 
					            locationControl: UIEventSource<Loc>,
 | 
				
			||||||
 | 
					            layoutToUse: LayoutConfig
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        this.hash = hash;
 | 
					        this.hash = hash;
 | 
				
			||||||
| 
						 | 
					@ -27,30 +34,9 @@ export default class SelectedFeatureHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // If the hash changes, set the selected element correctly
 | 
					        // If the hash changes, set the selected element correctly
 | 
				
			||||||
        function setSelectedElementFromHash(h){
 | 
					 | 
				
			||||||
            if (h === undefined || h === "") {
 | 
					 | 
				
			||||||
                // Hash has been cleared - we clear the selected element
 | 
					 | 
				
			||||||
                state.selectedElement.setData(undefined);
 | 
					 | 
				
			||||||
            }else{
 | 
					 | 
				
			||||||
                // we search the element to select
 | 
					 | 
				
			||||||
                const feature = state.allElements.ContainingFeatures.get(h)
 | 
					 | 
				
			||||||
                if(feature === undefined){
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                const currentlySeleced = state.selectedElement.data
 | 
					 | 
				
			||||||
                if(currentlySeleced === undefined){
 | 
					 | 
				
			||||||
                    state.selectedElement.setData(feature)
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if(currentlySeleced.properties?.id === feature.properties.id){
 | 
					 | 
				
			||||||
                    // We already have the right feature
 | 
					 | 
				
			||||||
                    return;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                state.selectedElement.setData(feature)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        hash.addCallback(setSelectedElementFromHash)
 | 
					        const self = this;
 | 
				
			||||||
 | 
					        hash.addCallback(() => self.setSelectedElementFromHash())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // IF the selected element changes, set the hash correctly
 | 
					        // IF the selected element changes, set the hash correctly
 | 
				
			||||||
| 
						 | 
					@ -67,40 +53,102 @@ export default class SelectedFeatureHandler {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        state.featurePipeline.newDataLoadedSignal.addCallbackAndRunD(_ => {
 | 
					        state.featurePipeline?.newDataLoadedSignal?.addCallbackAndRunD(_ => {
 | 
				
			||||||
            // New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet
 | 
					            // New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet
 | 
				
			||||||
            if(hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)){
 | 
					            if (hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)) {
 | 
				
			||||||
                // This is an invalid hash anyway
 | 
					                // This is an invalid hash anyway
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            if(state.selectedElement.data !== undefined){
 | 
					            if (state.selectedElement.data !== undefined) {
 | 
				
			||||||
                // We already have something selected
 | 
					                // We already have something selected
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            setSelectedElementFromHash(hash.data)
 | 
					            self.setSelectedElementFromHash()
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.initialLoad()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * On startup: check if the hash is loaded and eventually zoom to it
 | 
				
			||||||
 | 
					     * @private
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private initialLoad() {
 | 
				
			||||||
 | 
					        const hash = this.hash.data
 | 
				
			||||||
 | 
					        if (hash === undefined || hash === "" || hash.indexOf("-") >= 0) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (SelectedFeatureHandler._no_trigger_on.has(hash)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        OsmObject.DownloadObjectAsync(hash).then(obj => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                console.log("Downloaded selected object from OSM-API for initial load: ", hash)
 | 
				
			||||||
 | 
					                const geojson = obj.asGeoJson()
 | 
				
			||||||
 | 
					                this.state.allElements.addOrGetElement(geojson)
 | 
				
			||||||
 | 
					                this.state.selectedElement.setData(geojson)
 | 
				
			||||||
 | 
					                this.zoomToSelectedFeature()
 | 
				
			||||||
 | 
					            } catch (e) {
 | 
				
			||||||
 | 
					                console.error(e)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private setSelectedElementFromHash() {
 | 
				
			||||||
 | 
					        const state = this.state
 | 
				
			||||||
 | 
					        const h = this.hash.data
 | 
				
			||||||
 | 
					        if (h === undefined || h === "") {
 | 
				
			||||||
 | 
					            // Hash has been cleared - we clear the selected element
 | 
				
			||||||
 | 
					            state.selectedElement.setData(undefined);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // we search the element to select
 | 
				
			||||||
 | 
					            const feature = state.allElements.ContainingFeatures.get(h)
 | 
				
			||||||
 | 
					            if (feature === undefined) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            const currentlySeleced = state.selectedElement.data
 | 
				
			||||||
 | 
					            if (currentlySeleced === undefined) {
 | 
				
			||||||
 | 
					                state.selectedElement.setData(feature)
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (currentlySeleced.properties?.id === feature.properties.id) {
 | 
				
			||||||
 | 
					                // We already have the right feature
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            state.selectedElement.setData(feature)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // If a feature is selected via the hash, zoom there
 | 
					    // If a feature is selected via the hash, zoom there
 | 
				
			||||||
    public zoomToSelectedFeature(location: UIEventSource<Loc>) {
 | 
					    private zoomToSelectedFeature() {
 | 
				
			||||||
        const hash = this.hash.data;
 | 
					 | 
				
			||||||
        if (hash === undefined || SelectedFeatureHandler._no_trigger_on.has(hash)) {
 | 
					 | 
				
			||||||
            return; // No valid feature selected
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        // We should have a valid osm-ID and zoom to it... But we wrap it in try-catch to be sure
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
            OsmObject.DownloadObject(hash).addCallbackAndRunD(element => {
 | 
					        const selected = this.state.selectedElement.data
 | 
				
			||||||
                const centerpoint = element.centerpoint();
 | 
					        if(selected === undefined){
 | 
				
			||||||
                console.log("Zooming to location for select point: ", centerpoint)
 | 
					            return
 | 
				
			||||||
                location.data.lat = centerpoint[0]
 | 
					 | 
				
			||||||
                location.data.lon = centerpoint[1]
 | 
					 | 
				
			||||||
                location.ping();
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        } catch (e) {
 | 
					 | 
				
			||||||
            console.error("Could not download OSM-object with id", hash, " - probably a weird hash")
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const centerpoint= GeoOperations.centerpointCoordinates(selected)
 | 
				
			||||||
 | 
					        const location = this.state.locationControl;
 | 
				
			||||||
 | 
					        location.data.lon = centerpoint[0]
 | 
				
			||||||
 | 
					        location.data.lat = centerpoint[1]
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        const minZoom = Math.max(14, ...(this.state.layoutToUse?.layers?.map(l => l.minzoomVisible) ?? []))
 | 
				
			||||||
 | 
					        if(location.data.zoom < minZoom  ){
 | 
				
			||||||
 | 
					            location.data.zoom = minZoom
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        location.ping();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import TagRenderingAnswer from "../../UI/Popup/TagRenderingAnswer";
 | 
				
			||||||
import Combine from "../../UI/Base/Combine";
 | 
					import Combine from "../../UI/Base/Combine";
 | 
				
			||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
					import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
				
			||||||
import {ElementStorage} from "../ElementStorage";
 | 
					import {ElementStorage} from "../ElementStorage";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class TitleHandler {
 | 
					export default class TitleHandler {
 | 
				
			||||||
    constructor(state : {
 | 
					    constructor(state : {
 | 
				
			||||||
| 
						 | 
					@ -38,6 +39,9 @@ export default class TitleHandler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        currentTitle.addCallbackAndRunD(title => {
 | 
					        currentTitle.addCallbackAndRunD(title => {
 | 
				
			||||||
 | 
					            if(Utils.runningFromConsole){
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            document.title = title
 | 
					            document.title = title
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,6 @@ import State from "../State";
 | 
				
			||||||
import BaseUIElement from "../UI/BaseUIElement";
 | 
					import BaseUIElement from "../UI/BaseUIElement";
 | 
				
			||||||
import List from "../UI/Base/List";
 | 
					import List from "../UI/Base/List";
 | 
				
			||||||
import Title from "../UI/Base/Title";
 | 
					import Title from "../UI/Base/Title";
 | 
				
			||||||
import {UIEventSourceTools} from "./UIEventSource";
 | 
					 | 
				
			||||||
import AspectedRouting from "./Osm/aspectedRouting";
 | 
					 | 
				
			||||||
import {BBox} from "./BBox";
 | 
					import {BBox} from "./BBox";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface ExtraFuncParams {
 | 
					export interface ExtraFuncParams {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,10 +22,12 @@ export default class AllImageProviders {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    public static defaultKeys = [].concat(AllImageProviders.ImageAttributionSource.map(provider => provider.defaultKeyPrefixes))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<string, UIEventSource<ProvidedImage[]>>()
 | 
					    private static _cache: Map<string, UIEventSource<ProvidedImage[]>> = new Map<string, UIEventSource<ProvidedImage[]>>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static LoadImagesFor(tags: UIEventSource<any>, tagKey?: string): UIEventSource<ProvidedImage[]> {
 | 
					    public static LoadImagesFor(tags: UIEventSource<any>, tagKey?: string[]): UIEventSource<ProvidedImage[]> {
 | 
				
			||||||
        if (tags.data.id === undefined) {
 | 
					        if (tags.data.id === undefined) {
 | 
				
			||||||
            return undefined;
 | 
					            return undefined;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -44,7 +46,7 @@ export default class AllImageProviders {
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            let prefixes = imageProvider.defaultKeyPrefixes
 | 
					            let prefixes = imageProvider.defaultKeyPrefixes
 | 
				
			||||||
            if(tagKey !== undefined){
 | 
					            if(tagKey !== undefined){
 | 
				
			||||||
                prefixes = [...prefixes, tagKey]
 | 
					                prefixes = tagKey
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            const singleSource = imageProvider.GetRelevantUrls(tags, {
 | 
					            const singleSource = imageProvider.GetRelevantUrls(tags, {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,8 @@ export default class ImgurUploader {
 | 
				
			||||||
            files,
 | 
					            files,
 | 
				
			||||||
            function (url) {
 | 
					            function (url) {
 | 
				
			||||||
                console.log("File saved at", url);
 | 
					                console.log("File saved at", url);
 | 
				
			||||||
                self.success.setData([...self.success.data, url]);
 | 
					                self.success.data.push(url)
 | 
				
			||||||
 | 
					                self.success.ping();
 | 
				
			||||||
                self._handleSuccessUrl(url);
 | 
					                self._handleSuccessUrl(url);
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            function () {
 | 
					            function () {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,9 @@
 | 
				
			||||||
import ImageProvider, {ProvidedImage} from "./ImageProvider";
 | 
					import ImageProvider, {ProvidedImage} from "./ImageProvider";
 | 
				
			||||||
import BaseUIElement from "../../UI/BaseUIElement";
 | 
					import BaseUIElement from "../../UI/BaseUIElement";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					 | 
				
			||||||
import Svg from "../../Svg";
 | 
					import Svg from "../../Svg";
 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
import {LicenseInfo} from "./LicenseInfo";
 | 
					import {LicenseInfo} from "./LicenseInfo";
 | 
				
			||||||
import Constants from "../../Models/Constants";
 | 
					import Constants from "../../Models/Constants";
 | 
				
			||||||
import {fail} from "assert";
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class Mapillary extends ImageProvider {
 | 
					export class Mapillary extends ImageProvider {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +11,7 @@ export class Mapillary extends ImageProvider {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public static readonly singleton = new Mapillary();
 | 
					    public static readonly singleton = new Mapillary();
 | 
				
			||||||
    private static readonly valuePrefix = "https://a.mapillary.com"
 | 
					    private static readonly valuePrefix = "https://a.mapillary.com"
 | 
				
			||||||
    public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com","https://mapillary.com"]
 | 
					    public static readonly valuePrefixes = [Mapillary.valuePrefix, "http://mapillary.com","https://mapillary.com","http://www.mapillary.com","https://www.mapillary.com"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private static ExtractKeyFromURL(value: string, failIfNoMath = false): {
 | 
					    private static ExtractKeyFromURL(value: string, failIfNoMath = false): {
 | 
				
			||||||
        key: string,
 | 
					        key: string,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,6 +97,7 @@ export class Changes {
 | 
				
			||||||
            console.log("Is already uploading... Abort")
 | 
					            console.log("Is already uploading... Abort")
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        console.log("Uploading changes due to: ", flushreason)
 | 
				
			||||||
        this.isUploading.setData(true)
 | 
					        this.isUploading.setData(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.flushChangesAsync()
 | 
					        this.flushChangesAsync()
 | 
				
			||||||
| 
						 | 
					@ -287,7 +288,7 @@ export class Changes {
 | 
				
			||||||
                    v = undefined;
 | 
					                    v = undefined;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const oldV = obj.type[k]
 | 
					                const oldV = obj.tags[k]
 | 
				
			||||||
                if (oldV === v) {
 | 
					                if (oldV === v) {
 | 
				
			||||||
                    continue;
 | 
					                    continue;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,6 @@ import Svg from "../../Svg";
 | 
				
			||||||
import Img from "../../UI/Base/Img";
 | 
					import Img from "../../UI/Base/Img";
 | 
				
			||||||
import {Utils} from "../../Utils";
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
import {OsmObject} from "./OsmObject";
 | 
					import {OsmObject} from "./OsmObject";
 | 
				
			||||||
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
					 | 
				
			||||||
import {Changes} from "./Changes";
 | 
					import {Changes} from "./Changes";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class UserDetails {
 | 
					export default class UserDetails {
 | 
				
			||||||
| 
						 | 
					@ -97,7 +96,6 @@ export class OsmConnection {
 | 
				
			||||||
                self.AttemptLogin()
 | 
					                self.AttemptLogin()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        this.isLoggedIn.addCallbackAndRunD(li => console.log("User is logged in!", li))
 | 
					 | 
				
			||||||
        this._dryRun = options.dryRun;
 | 
					        this._dryRun = options.dryRun;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.updateAuthObject();
 | 
					        this.updateAuthObject();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,7 @@ export abstract class OsmObject {
 | 
				
			||||||
                continue;
 | 
					                continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            const v = this.tags[key];
 | 
					            const v = this.tags[key];
 | 
				
			||||||
            if (v !== "") {
 | 
					            if (v !== "" && v !== undefined) {
 | 
				
			||||||
                tags += '        <tag k="' + Utils.EncodeXmlValue(key) + '" v="' + Utils.EncodeXmlValue(this.tags[key]) + '"/>\n'
 | 
					                tags += '        <tag k="' + Utils.EncodeXmlValue(key) + '" v="' + Utils.EncodeXmlValue(this.tags[key]) + '"/>\n'
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig";
 | 
				
			||||||
import {UIEventSource} from "../UIEventSource";
 | 
					import {UIEventSource} from "../UIEventSource";
 | 
				
			||||||
import {QueryParameters} from "../Web/QueryParameters";
 | 
					import {QueryParameters} from "../Web/QueryParameters";
 | 
				
			||||||
import Constants from "../../Models/Constants";
 | 
					import Constants from "../../Models/Constants";
 | 
				
			||||||
 | 
					import {Utils} from "../../Utils";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class FeatureSwitchState {
 | 
					export default class FeatureSwitchState {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -137,7 +138,7 @@ export default class FeatureSwitchState {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let testingDefaultValue = false;
 | 
					        let testingDefaultValue = false;
 | 
				
			||||||
        if (this.featureSwitchApiURL.data !== "osm-test" &&
 | 
					        if (this.featureSwitchApiURL.data !== "osm-test" && !Utils.runningFromConsole &&
 | 
				
			||||||
            (location.hostname === "localhost" || location.hostname === "127.0.0.1")) {
 | 
					            (location.hostname === "localhost" || location.hostname === "127.0.0.1")) {
 | 
				
			||||||
            testingDefaultValue = true
 | 
					            testingDefaultValue = true
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -286,6 +286,9 @@ export class UIEventSource<T> {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public stabilized(millisToStabilize): UIEventSource<T> {
 | 
					    public stabilized(millisToStabilize): UIEventSource<T> {
 | 
				
			||||||
 | 
					        if(Utils.runningFromConsole){
 | 
				
			||||||
 | 
					            return this;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const newSource = new UIEventSource<T>(this.data);
 | 
					        const newSource = new UIEventSource<T>(this.data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -335,20 +338,3 @@ export class UIEventSource<T> {
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
export class UIEventSourceTools {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static readonly _download_cache = new Map<string, UIEventSource<any>>()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public static downloadJsonCached(url: string): UIEventSource<any> {
 | 
					 | 
				
			||||||
        const cached = UIEventSourceTools._download_cache.get(url)
 | 
					 | 
				
			||||||
        if (cached !== undefined) {
 | 
					 | 
				
			||||||
            return cached;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        const src = new UIEventSource<any>(undefined)
 | 
					 | 
				
			||||||
        UIEventSourceTools._download_cache.set(url, src)
 | 
					 | 
				
			||||||
        Utils.downloadJson(url).then(r => src.setData(r))
 | 
					 | 
				
			||||||
        return src;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,45 +0,0 @@
 | 
				
			||||||
import {UIEventSource} from "../../Logic/UIEventSource";
 | 
					 | 
				
			||||||
import BaseUIElement from "../BaseUIElement";
 | 
					 | 
				
			||||||
import {VariableUiElement} from "../Base/VariableUIElement";
 | 
					 | 
				
			||||||
import Translations from "../i18n/Translations";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Shows that 'images are uploading', 'all images are uploaded' as relevant...
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
export default class UploadFlowStateUI extends VariableUiElement {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    constructor(queue: UIEventSource<string[]>, failed: UIEventSource<string[]>, success: UIEventSource<string[]>) {
 | 
					 | 
				
			||||||
        const t = Translations.t.image;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        super(
 | 
					 | 
				
			||||||
            queue.map(queue => {
 | 
					 | 
				
			||||||
                const failedReasons = failed.data
 | 
					 | 
				
			||||||
                const successCount = success.data.length
 | 
					 | 
				
			||||||
                const pendingCount = queue.length - successCount - failedReasons.length;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                let stateMessages: BaseUIElement[] = []
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (pendingCount == 1) {
 | 
					 | 
				
			||||||
                    stateMessages.push(t.uploadingPicture.Clone().SetClass("alert"))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (pendingCount > 1) {
 | 
					 | 
				
			||||||
                    stateMessages.push(t.uploadingMultiple.Subs({count: "" + pendingCount}).SetClass("alert"))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (failedReasons.length > 0) {
 | 
					 | 
				
			||||||
                    stateMessages.push(t.uploadFailed.Clone().SetClass("alert"))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                if (successCount > 0 && pendingCount == 0) {
 | 
					 | 
				
			||||||
                    stateMessages.push(t.uploadDone.SetClass("thanks"))
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                stateMessages.forEach(msg => msg.SetStyle("display: block ruby"))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                return stateMessages
 | 
					 | 
				
			||||||
            }, [failed, success])
 | 
					 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,7 @@ import ScrollableFullScreen from "./Base/ScrollableFullScreen";
 | 
				
			||||||
import Translations from "./i18n/Translations";
 | 
					import Translations from "./i18n/Translations";
 | 
				
			||||||
import SimpleAddUI from "./BigComponents/SimpleAddUI";
 | 
					import SimpleAddUI from "./BigComponents/SimpleAddUI";
 | 
				
			||||||
import StrayClickHandler from "../Logic/Actors/StrayClickHandler";
 | 
					import StrayClickHandler from "../Logic/Actors/StrayClickHandler";
 | 
				
			||||||
 | 
					import Lazy from "./Base/Lazy";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class DefaultGuiState {
 | 
					export class DefaultGuiState {
 | 
				
			||||||
    public readonly welcomeMessageIsOpened;
 | 
					    public readonly welcomeMessageIsOpened;
 | 
				
			||||||
| 
						 | 
					@ -81,12 +82,22 @@ export default class DefaultGUI {
 | 
				
			||||||
    constructor(state: FeaturePipelineState, guiState: DefaultGuiState) {
 | 
					    constructor(state: FeaturePipelineState, guiState: DefaultGuiState) {
 | 
				
			||||||
        this.state = state;
 | 
					        this.state = state;
 | 
				
			||||||
        this._guiState = guiState;
 | 
					        this._guiState = guiState;
 | 
				
			||||||
        const self = this;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (state.layoutToUse.customCss !== undefined) {
 | 
					        if (state.layoutToUse.customCss !== undefined) {
 | 
				
			||||||
            Utils.LoadCustomCss(state.layoutToUse.customCss);
 | 
					            Utils.LoadCustomCss(state.layoutToUse.customCss);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.SetupUIElements();
 | 
				
			||||||
 | 
					        this.SetupMap()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private SetupMap(){
 | 
				
			||||||
 | 
					        const state = this.state;
 | 
				
			||||||
 | 
					        const guiState = this._guiState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Attach the map
 | 
					        // Attach the map
 | 
				
			||||||
        state.mainMapObject.SetClass("w-full h-full")
 | 
					        state.mainMapObject.SetClass("w-full h-full")
 | 
				
			||||||
            .AttachTo("leafletDiv")
 | 
					            .AttachTo("leafletDiv")
 | 
				
			||||||
| 
						 | 
					@ -96,8 +107,28 @@ export default class DefaultGUI {
 | 
				
			||||||
            state
 | 
					            state
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.InitWelcomeMessage();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        new ShowDataLayer({
 | 
				
			||||||
 | 
					            leafletMap: state.leafletMap,
 | 
				
			||||||
 | 
					            layerToShow: AllKnownLayers.sharedLayers.get("home_location"),
 | 
				
			||||||
 | 
					            features: state.homeLocation,
 | 
				
			||||||
 | 
					            enablePopups: false,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        state.leafletMap.addCallbackAndRunD(_ => {
 | 
				
			||||||
 | 
					            // Lets assume that all showDataLayers are initialized at this point
 | 
				
			||||||
 | 
					            state.selectedElement.ping()
 | 
				
			||||||
 | 
					            State.state.locationControl.ping();
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private SetupUIElements(){
 | 
				
			||||||
 | 
					        const state = this.state;
 | 
				
			||||||
 | 
					        const guiState = this._guiState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const self =this
 | 
				
			||||||
        Toggle.If(state.featureSwitchUserbadge,
 | 
					        Toggle.If(state.featureSwitchUserbadge,
 | 
				
			||||||
            () => new UserBadge(state)
 | 
					            () => new UserBadge(state)
 | 
				
			||||||
        ).AttachTo("userbadge")
 | 
					        ).AttachTo("userbadge")
 | 
				
			||||||
| 
						 | 
					@ -119,36 +150,21 @@ export default class DefaultGUI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new Toggle(self.InitWelcomeMessage(),
 | 
					        new Toggle(new Lazy(() => self.InitWelcomeMessage()),
 | 
				
			||||||
            Toggle.If(state.featureSwitchIframePopoutEnabled, iframePopout),
 | 
					            Toggle.If(state.featureSwitchIframePopoutEnabled, iframePopout),
 | 
				
			||||||
            state.featureSwitchWelcomeMessage
 | 
					            state.featureSwitchWelcomeMessage
 | 
				
			||||||
        ).AttachTo("messagesbox");
 | 
					        ).AttachTo("messagesbox");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        new LeftControls(state, guiState).AttachTo("bottom-left");
 | 
					        new LeftControls(state, guiState).AttachTo("bottom-left");
 | 
				
			||||||
        new RightControls(state).AttachTo("bottom-right");
 | 
					        new RightControls(state).AttachTo("bottom-right");
 | 
				
			||||||
        State.state.locationControl.ping();
 | 
					
 | 
				
			||||||
        new CenterMessageBox(state).AttachTo("centermessage");
 | 
					        new CenterMessageBox(state).AttachTo("centermessage");
 | 
				
			||||||
        document
 | 
					        document
 | 
				
			||||||
            .getElementById("centermessage")
 | 
					            .getElementById("centermessage")
 | 
				
			||||||
            .classList.add("pointer-events-none");
 | 
					            .classList.add("pointer-events-none");
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        new ShowDataLayer({
 | 
					 | 
				
			||||||
            leafletMap: state.leafletMap,
 | 
					 | 
				
			||||||
            layerToShow: AllKnownLayers.sharedLayers.get("home_location"),
 | 
					 | 
				
			||||||
            features:            state.homeLocation,
 | 
					 | 
				
			||||||
            enablePopups: false,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        state.leafletMap.addCallbackAndRunD(_ => {
 | 
					 | 
				
			||||||
            // Lets assume that all showDataLayers are initialized at this point
 | 
					 | 
				
			||||||
            state.selectedElement.ping()
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private InitWelcomeMessage() {
 | 
					    private InitWelcomeMessage() : BaseUIElement{
 | 
				
			||||||
        const isOpened = this._guiState.welcomeMessageIsOpened
 | 
					        const isOpened = this._guiState.welcomeMessageIsOpened
 | 
				
			||||||
        const fullOptions = new FullWelcomePaneWithTabs(isOpened, this._guiState.welcomeMessageOpenedTab, this.state);
 | 
					        const fullOptions = new FullWelcomePaneWithTabs(isOpened, this._guiState.welcomeMessageOpenedTab, this.state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -180,7 +196,7 @@ export default class DefaultGUI {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public setupClickDialogOnMap(filterViewIsOpened: UIEventSource<boolean>, state: FeaturePipelineState) {
 | 
					    public setupClickDialogOnMap(filterViewIsOpened: UIEventSource<boolean>, state: FeaturePipelineState) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function setup(){
 | 
					        function setup() {
 | 
				
			||||||
            let presetCount = 0;
 | 
					            let presetCount = 0;
 | 
				
			||||||
            for (const layer of state.layoutToUse.layers) {
 | 
					            for (const layer of state.layoutToUse.layers) {
 | 
				
			||||||
                for (const preset of layer.presets) {
 | 
					                for (const preset of layer.presets) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,9 @@ import ImageProvider from "../../Logic/ImageProviders/ImageProvider";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ImageCarousel extends Toggle {
 | 
					export class ImageCarousel extends Toggle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(images: UIEventSource<{ key: string, url: string, provider: ImageProvider }[]>, tags: UIEventSource<any>) {
 | 
					    constructor(images: UIEventSource<{ key: string, url: string, provider: ImageProvider }[]>, 
 | 
				
			||||||
 | 
					                tags: UIEventSource<any>,
 | 
				
			||||||
 | 
					                keys: string[]) {
 | 
				
			||||||
        const uiElements = images.map((imageURLS: { key: string, url: string, provider: ImageProvider }[]) => {
 | 
					        const uiElements = images.map((imageURLS: { key: string, url: string, provider: ImageProvider }[]) => {
 | 
				
			||||||
            const uiElements: BaseUIElement[] = [];
 | 
					            const uiElements: BaseUIElement[] = [];
 | 
				
			||||||
            for (const url of imageURLS) {
 | 
					            for (const url of imageURLS) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,14 +9,23 @@ import LicensePicker from "../BigComponents/LicensePicker";
 | 
				
			||||||
import Toggle from "../Input/Toggle";
 | 
					import Toggle from "../Input/Toggle";
 | 
				
			||||||
import FileSelectorButton from "../Input/FileSelectorButton";
 | 
					import FileSelectorButton from "../Input/FileSelectorButton";
 | 
				
			||||||
import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader";
 | 
					import ImgurUploader from "../../Logic/ImageProviders/ImgurUploader";
 | 
				
			||||||
import UploadFlowStateUI from "../BigComponents/UploadFlowStateUI";
 | 
					 | 
				
			||||||
import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
 | 
					import ChangeTagAction from "../../Logic/Osm/Actions/ChangeTagAction";
 | 
				
			||||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
 | 
					import LayerConfig from "../../Models/ThemeConfig/LayerConfig";
 | 
				
			||||||
import {FixedUiElement} from "../Base/FixedUiElement";
 | 
					import {FixedUiElement} from "../Base/FixedUiElement";
 | 
				
			||||||
 | 
					import {VariableUiElement} from "../Base/VariableUIElement";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class ImageUploadFlow extends Toggle {
 | 
					export class ImageUploadFlow extends Toggle {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private static readonly uploadCountsPerId = new Map<string, UIEventSource<number>>()
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    constructor(tagsSource: UIEventSource<any>, imagePrefix: string = "image", text: string = undefined) {
 | 
					    constructor(tagsSource: UIEventSource<any>, imagePrefix: string = "image", text: string = undefined) {
 | 
				
			||||||
 | 
					        const perId = ImageUploadFlow.uploadCountsPerId
 | 
				
			||||||
 | 
					        const id = tagsSource.data.id
 | 
				
			||||||
 | 
					        if(!perId.has(id)){
 | 
				
			||||||
 | 
					            perId.set(id, new UIEventSource<number>(0))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const uploadedCount = perId.get(id)
 | 
				
			||||||
        const uploader = new ImgurUploader(url => {
 | 
					        const uploader = new ImgurUploader(url => {
 | 
				
			||||||
            // A file was uploaded - we add it to the tags of the object
 | 
					            // A file was uploaded - we add it to the tags of the object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +39,8 @@ export class ImageUploadFlow extends Toggle {
 | 
				
			||||||
                key = imagePrefix + ":" + freeIndex;
 | 
					                key = imagePrefix + ":" + freeIndex;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            console.log("Adding image:" + key, url);
 | 
					            console.log("Adding image:" + key, url);
 | 
				
			||||||
 | 
					            uploadedCount.data ++
 | 
				
			||||||
 | 
					            uploadedCount.ping()
 | 
				
			||||||
            Promise.resolve(State.state.changes
 | 
					            Promise.resolve(State.state.changes
 | 
				
			||||||
                .applyAction(new ChangeTagAction(
 | 
					                .applyAction(new ChangeTagAction(
 | 
				
			||||||
                    tags.id, new Tag(key, url), tagsSource.data,
 | 
					                    tags.id, new Tag(key, url), tagsSource.data,
 | 
				
			||||||
| 
						 | 
					@ -40,10 +51,6 @@ export class ImageUploadFlow extends Toggle {
 | 
				
			||||||
                )))
 | 
					                )))
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        uploader.queue.addCallbackD(q => console.log("Image upload queue is ", q))
 | 
					 | 
				
			||||||
        uploader.failed.addCallbackD(q => console.log("Image upload fail list is ", q))
 | 
					 | 
				
			||||||
        uploader.success.addCallbackD(q => console.log("Image upload success list is ", q))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const licensePicker = new LicensePicker()
 | 
					        const licensePicker = new LicensePicker()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const t = Translations.t.image;
 | 
					        const t = Translations.t.image;
 | 
				
			||||||
| 
						 | 
					@ -105,10 +112,33 @@ export class ImageUploadFlow extends Toggle {
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const uploadStateUi = new UploadFlowStateUI(uploader.queue, uploader.failed, uploader.success)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const uploadFlow: BaseUIElement = new Combine([
 | 
					        const uploadFlow: BaseUIElement = new Combine([
 | 
				
			||||||
            uploadStateUi,
 | 
					            new VariableUiElement(uploader.queue.map(q => q.length).map(l => {
 | 
				
			||||||
 | 
					                if(l == 0){
 | 
				
			||||||
 | 
					                    return undefined;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if(l == 1){
 | 
				
			||||||
 | 
					                   return t.uploadingPicture.Clone().SetClass("alert")
 | 
				
			||||||
 | 
					                }else{
 | 
				
			||||||
 | 
					                    return t.uploadingMultiple.Subs({count: "" + l}).SetClass("alert")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					            new VariableUiElement(uploader.failed.map(q => q.length).map(l => {
 | 
				
			||||||
 | 
					                if(l==0){
 | 
				
			||||||
 | 
					                    return undefined
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return t.uploadFailed.Clone().SetClass("alert");
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					            new VariableUiElement(uploadedCount.map(l => {
 | 
				
			||||||
 | 
					                if(l == 0){
 | 
				
			||||||
 | 
					                    return  undefined;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if(l == 1){
 | 
				
			||||||
 | 
					                    return t.uploadDone.Clone().SetClass("thanks");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return t.uploadMultipleDone.Subs({count: l}).SetClass("thanks")
 | 
				
			||||||
 | 
					            })),
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            fileSelector,
 | 
					            fileSelector,
 | 
				
			||||||
            Translations.t.image.respectPrivacy.Clone().SetStyle("font-size:small;"),
 | 
					            Translations.t.image.respectPrivacy.Clone().SetStyle("font-size:small;"),
 | 
				
			||||||
            licensePicker
 | 
					            licensePicker
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -83,15 +83,15 @@ export default class SpecialVisualizations {
 | 
				
			||||||
                docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)",
 | 
					                docs: "Creates an image carousel for the given sources. An attempt will be made to guess what source is used. Supported: Wikidata identifiers, Wikipedia pages, Wikimedia categories, IMGUR (with attribution, direct links)",
 | 
				
			||||||
                args: [{
 | 
					                args: [{
 | 
				
			||||||
                    name: "image key/prefix (multiple values allowed if comma-seperated)",
 | 
					                    name: "image key/prefix (multiple values allowed if comma-seperated)",
 | 
				
			||||||
                    defaultValue: "image",
 | 
					                    defaultValue: AllImageProviders.defaultKeys.join(","),
 | 
				
			||||||
                    doc: "The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc... "
 | 
					                    doc: "The keys given to the images, e.g. if <span class='literal-code'>image</span> is given, the first picture URL will be added as <span class='literal-code'>image</span>, the second as <span class='literal-code'>image:0</span>, the third as <span class='literal-code'>image:1</span>, etc... "
 | 
				
			||||||
                }],
 | 
					                }],
 | 
				
			||||||
                constr: (state: State, tags, args) => {
 | 
					                constr: (state: State, tags, args) => {
 | 
				
			||||||
                    let imagePrefixes = undefined;
 | 
					                    let imagePrefixes: string[] = undefined;
 | 
				
			||||||
                    if(args.length > 0){
 | 
					                    if(args.length > 0){
 | 
				
			||||||
                        imagePrefixes = args;
 | 
					                        imagePrefixes = [].concat(...args.map(a => a.split(",")));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags);
 | 
					                    return new ImageCarousel(AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, imagePrefixes);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Utils.ts
									
										
									
									
									
								
							| 
						 | 
					@ -330,7 +330,7 @@ export class Utils {
 | 
				
			||||||
                return cached.promise
 | 
					                return cached.promise
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const promise = Utils.downloadJson(url, headers)
 | 
					        const promise = /*NO AWAIT as we work with the promise directly */Utils.downloadJson(url, headers)
 | 
				
			||||||
        Utils._download_cache.set(url, {promise, timestamp: new Date().getTime()})
 | 
					        Utils._download_cache.set(url, {promise, timestamp: new Date().getTime()})
 | 
				
			||||||
        return await promise
 | 
					        return await promise
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -157,10 +157,6 @@
 | 
				
			||||||
        ]
 | 
					        ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "titleIcons": [
 | 
					    "titleIcons": [
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "render": "<a href='https://fietsambassade.gent.be/' target='_blank'><img src='./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg'/></a>",
 | 
					 | 
				
			||||||
            "condition": "operator=De Fietsambassade Gent"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "render": "<a href='https://fietsambassade.gent.be/' target='_blank'><img src='./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg'/></a>",
 | 
					            "render": "<a href='https://fietsambassade.gent.be/' target='_blank'><img src='./assets/themes/cyclofix/fietsambassade_gent_logo_small.svg'/></a>",
 | 
				
			||||||
            "condition": "operator=De Fietsambassade Gent"
 | 
					            "condition": "operator=De Fietsambassade Gent"
 | 
				
			||||||
| 
						 | 
					@ -178,6 +174,10 @@
 | 
				
			||||||
            "condition": "service:bicycle:diy=yes",
 | 
					            "condition": "service:bicycle:diy=yes",
 | 
				
			||||||
            "render": "<img src='./assets/layers/bike_shop/tools.svg'/>"
 | 
					            "render": "<img src='./assets/layers/bike_shop/tools.svg'/>"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "condition": "service:bicycle:cleaning=yes",
 | 
				
			||||||
 | 
					            "render": "<img src='./assets/layers/bike_cleaning/bike_cleaning_icon.svg'/>"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "defaults"
 | 
					        "defaults"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "description": {
 | 
					    "description": {
 | 
				
			||||||
| 
						 | 
					@ -314,13 +314,6 @@
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "id": "bike_shop-access"
 | 
					            "id": "bike_shop-access"
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            "render": "Enkel voor {access}",
 | 
					 | 
				
			||||||
            "freeform": {
 | 
					 | 
				
			||||||
                "key": "access"
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            "id": "bike_shop-access"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "id": "bike_repair_sells-bikes",
 | 
					            "id": "bike_repair_sells-bikes",
 | 
				
			||||||
            "question": {
 | 
					            "question": {
 | 
				
			||||||
| 
						 | 
					@ -670,6 +663,32 @@
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            ]
 | 
					            ]
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            "question": "How much does it cost to use the cleaning service?",
 | 
				
			||||||
 | 
					            "render": "Using the cleaning service costs {charge}",
 | 
				
			||||||
 | 
					            "freeform": {
 | 
				
			||||||
 | 
					                "key": "service:bicycle:cleaning:charge",
 | 
				
			||||||
 | 
					                "addExtraTags": [
 | 
				
			||||||
 | 
					                    "service:bicycle:cleaning:fee=yes"
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "mappings": [
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "if": "service:bicycle:cleaning:fee=no&service:bicycle:cleaning:charge=",
 | 
				
			||||||
 | 
					                    "then": "The cleaning service is free to use"
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "if": "service:bicycle:cleaning:fee=no&",
 | 
				
			||||||
 | 
					                    "then": "Free to use",
 | 
				
			||||||
 | 
					                    "hideInAnswer": true
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "if": "service:bicycle:cleaning:fee=yes",
 | 
				
			||||||
 | 
					                    "then": "The cleaning service has a fee"
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            "id": "bike_cleaning-service:bicycle:cleaning:charge"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "presets": [
 | 
					    "presets": [
 | 
				
			||||||
| 
						 | 
					@ -708,13 +727,19 @@
 | 
				
			||||||
            "badge": true
 | 
					            "badge": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "if": "opening_hours~*",
 | 
					            "if": "service:bicycle:pump=yes",
 | 
				
			||||||
            "then": "isOpen",
 | 
					            "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg",
 | 
				
			||||||
            "badge": true
 | 
					            "badge": true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            "if": "service:bicycle:pump=yes",
 | 
					            "if": {
 | 
				
			||||||
            "then": "circle:#e2783d;./assets/layers/bike_repair_station/pump.svg",
 | 
					                "and": [
 | 
				
			||||||
 | 
					                    "service:bicycle:cleaning~*"
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            "then": {
 | 
				
			||||||
 | 
					                "render": "./assets/layers/bike_cleaning/bike_cleaning_icon.svg"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            "badge": true
 | 
					            "badge": true
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,26 +42,12 @@
 | 
				
			||||||
      "iconSize": "20,20,center",
 | 
					      "iconSize": "20,20,center",
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        "all_tags"
 | 
					        "all_tags"
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": {
 | 
					 | 
				
			||||||
            "render": "circle:red",
 | 
					 | 
				
			||||||
            "mappings": [
 | 
					 | 
				
			||||||
              {
 | 
					 | 
				
			||||||
                "if": "_has_closeby_feature=yes",
 | 
					 | 
				
			||||||
                "then": "circle:#008000aa"
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            ]
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "iconSize": "20,20,center",
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  "hideFromOverview": true,
 | 
					  "hideFromOverview": true,
 | 
				
			||||||
  "defaultBackgroundId": "HDM_HOT"
 | 
					  "defaultBackgroundId": "HDM_HOT",
 | 
				
			||||||
 | 
					  "clustering": {
 | 
				
			||||||
 | 
					    "maxZoom": 0
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  "id": "buurtnatuur",
 | 
					  "id": "buurtnatuur",
 | 
				
			||||||
  "title": {
 | 
					  "title": {
 | 
				
			||||||
    "#": "DO NOT TRANSLATE THIS THEME - this one is only meant to be in dutch!",
 | 
					 | 
				
			||||||
    "nl": "Breng jouw buurtnatuur in kaart"
 | 
					    "nl": "Breng jouw buurtnatuur in kaart"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "shortDescription": {
 | 
					  "shortDescription": {
 | 
				
			||||||
| 
						 | 
					@ -124,19 +123,6 @@
 | 
				
			||||||
            "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt"
 | 
					            "nl": "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": {
 | 
					 | 
				
			||||||
            "render": "circle:#ffffff;./assets/themes/buurtnatuur/nature_reserve.svg"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "iconSize": {
 | 
					 | 
				
			||||||
            "render": "50,50,center"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -235,19 +221,6 @@
 | 
				
			||||||
            "nl": "Voeg een ontbrekend park toe"
 | 
					            "nl": "Voeg een ontbrekend park toe"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": {
 | 
					 | 
				
			||||||
            "render": "circle:#ffffff;./assets/themes/buurtnatuur/park.svg"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "iconSize": {
 | 
					 | 
				
			||||||
            "render": "40,40,center"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -365,19 +338,6 @@
 | 
				
			||||||
            "nl": "Voeg een ontbrekend bos toe aan de kaart"
 | 
					            "nl": "Voeg een ontbrekend bos toe aan de kaart"
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": {
 | 
					 | 
				
			||||||
            "render": "circle:#ffffff;./assets/themes/buurtnatuur/forest.svg"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "iconSize": {
 | 
					 | 
				
			||||||
            "render": "40,40,center"
 | 
					 | 
				
			||||||
          },
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "viewpoint"
 | 
					    "viewpoint"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,14 +84,6 @@
 | 
				
			||||||
      "width": "10",
 | 
					      "width": "10",
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        "images"
 | 
					        "images"
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": "./assets/themes/cyclestreets/F111.svg",
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -144,14 +136,6 @@
 | 
				
			||||||
      "width": "5",
 | 
					      "width": "5",
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        "images"
 | 
					        "images"
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": "./assets/themes/cyclestreets/F113.svg",
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
| 
						 | 
					@ -217,14 +201,6 @@
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      "tagRenderings": [
 | 
					      "tagRenderings": [
 | 
				
			||||||
        "images"
 | 
					        "images"
 | 
				
			||||||
      ],
 | 
					 | 
				
			||||||
      "mapRendering": [
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
          "icon": "./assets/svg/pencil.svg",
 | 
					 | 
				
			||||||
          "location": [
 | 
					 | 
				
			||||||
            "point"
 | 
					 | 
				
			||||||
          ]
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      ]
 | 
					      ]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,8 @@
 | 
				
			||||||
        "ccb": "under the CC-BY-license",
 | 
					        "ccb": "under the CC-BY-license",
 | 
				
			||||||
        "uploadFailed": "Could not upload your picture. Are you connected to the Internet, and allow third party API's? The Brave browser or the uMatrix plugin might block them.",
 | 
					        "uploadFailed": "Could not upload your picture. Are you connected to the Internet, and allow third party API's? The Brave browser or the uMatrix plugin might block them.",
 | 
				
			||||||
        "respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
 | 
					        "respectPrivacy": "Do not photograph people nor license plates. Do not upload Google Maps, Google Streetview or other copyrighted sources.",
 | 
				
			||||||
        "uploadDone": "<span class='thanks'>Your picture has been added. Thanks for helping out!</span>",
 | 
					        "uploadDone": "Your picture has been added. Thanks for helping out!",
 | 
				
			||||||
 | 
					        "uploadMultipleDone": "{count} pictures have been added. Thanks for helping out!",
 | 
				
			||||||
        "dontDelete": "Cancel",
 | 
					        "dontDelete": "Cancel",
 | 
				
			||||||
        "doDelete": "Remove image",
 | 
					        "doDelete": "Remove image",
 | 
				
			||||||
        "isDeleted": "Deleted",
 | 
					        "isDeleted": "Deleted",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2376,7 +2376,7 @@
 | 
				
			||||||
                "question": "Is this street lit?"
 | 
					                "question": "Is this street lit?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "width:carriageway": {
 | 
					            "width:carriageway": {
 | 
				
			||||||
                "question": "What is the carriage width of this road (in meters)?",
 | 
					                "question": "What is the carriage width of this road (in meters)?<br/><span class='subtle'>This is measured curb to curb and thus includes the width of parallell parking lanes</span>",
 | 
				
			||||||
                "render": "The carriage width of this road is <strong>{width:carriageway}m</strong>"
 | 
					                "render": "The carriage width of this road is <strong>{width:carriageway}m</strong>"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2329,7 +2329,7 @@
 | 
				
			||||||
                "question": "Is deze weg verlicht?"
 | 
					                "question": "Is deze weg verlicht?"
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            "width:carriageway": {
 | 
					            "width:carriageway": {
 | 
				
			||||||
                "question": "Hoe breed is de rijbaan in deze straat (in meters)?",
 | 
					                "question": "Hoe breed is de rijbaan in deze straat (in meters)?<br/><span class='subtle'>Dit is </span><br/><span class='subtle'>Meet dit van stoepsteen tot stoepsteen, dus inclusief een parallelle parkeerstrook</span>",
 | 
				
			||||||
                "render": "De breedte van deze rijbaan in deze straat is <strong>{width:carriageway}m</strong>"
 | 
					                "render": "De breedte van deze rijbaan in deze straat is <strong>{width:carriageway}m</strong>"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -233,7 +233,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Wer betreibt diesen Ort?",
 | 
					                    "question": "Wer betreibt diesen Ort?",
 | 
				
			||||||
                    "render": "Dieser Ort wird betrieben von {operator}"
 | 
					                    "render": "Dieser Ort wird betrieben von {operator}"
 | 
				
			||||||
| 
						 | 
					@ -249,6 +250,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "Hat dieser Ort eine Stromversorgung?"
 | 
					                    "question": "Hat dieser Ort eine Stromversorgung?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Finden Sie Plätze zum Übernachten mit Ihrem Wohnmobil",
 | 
					        "shortDescription": "Finden Sie Plätze zum Übernachten mit Ihrem Wohnmobil",
 | 
				
			||||||
        "title": "Wohnmobilstellplätze"
 | 
					        "title": "Wohnmobilstellplätze"
 | 
				
			||||||
| 
						 | 
					@ -428,20 +430,7 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "overrideAll": {
 | 
					        "overrideAll": {
 | 
				
			||||||
            "units+": {
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					 | 
				
			||||||
                    "applicableUnits": {
 | 
					 | 
				
			||||||
                        "0": {
 | 
					 | 
				
			||||||
                            "human": " Meter"
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        "1": {
 | 
					 | 
				
			||||||
                            "human": " Fuß"
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "roamingRenderings": {
 | 
					 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?"
 | 
					                    "question": "Gibt es eine (inoffizielle) Website mit mehr Informationen (z.B. Topos)?"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -548,6 +537,19 @@
 | 
				
			||||||
                    "question": "Gibt es hier eine Speedkletter-Wand?"
 | 
					                    "question": "Gibt es hier eine Speedkletter-Wand?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            "units+": {
 | 
				
			||||||
 | 
					                "0": {
 | 
				
			||||||
 | 
					                    "applicableUnits": {
 | 
				
			||||||
 | 
					                        "0": {
 | 
				
			||||||
 | 
					                            "human": " Meter"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "1": {
 | 
				
			||||||
 | 
					                            "human": " Fuß"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "title": "Offene Kletterkarte"
 | 
					        "title": "Offene Kletterkarte"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "cycle_highways": {
 | 
					    "cycle_highways": {
 | 
				
			||||||
| 
						 | 
					@ -594,7 +596,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -616,6 +619,7 @@
 | 
				
			||||||
                    "question": "Wann wird diese Straße eine Fahrradstraße?",
 | 
					                    "question": "Wann wird diese Straße eine Fahrradstraße?",
 | 
				
			||||||
                    "render": "Diese Straße wird am {cyclestreet:start_date} zu einer Fahrradstraße"
 | 
					                    "render": "Diese Straße wird am {cyclestreet:start_date} zu einer Fahrradstraße"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Eine Karte von Fahrradstraßen",
 | 
					        "shortDescription": "Eine Karte von Fahrradstraßen",
 | 
				
			||||||
        "title": "Fahrradstraßen"
 | 
					        "title": "Fahrradstraßen"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -233,7 +233,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Who operates this place?",
 | 
					                    "question": "Who operates this place?",
 | 
				
			||||||
                    "render": "This place is operated by {operator}"
 | 
					                    "render": "This place is operated by {operator}"
 | 
				
			||||||
| 
						 | 
					@ -249,6 +250,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "Does this place have a power supply?"
 | 
					                    "question": "Does this place have a power supply?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Find sites to spend the night with your camper",
 | 
					        "shortDescription": "Find sites to spend the night with your camper",
 | 
				
			||||||
        "title": "Campersites"
 | 
					        "title": "Campersites"
 | 
				
			||||||
| 
						 | 
					@ -452,20 +454,7 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "overrideAll": {
 | 
					        "overrideAll": {
 | 
				
			||||||
            "units+": {
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					 | 
				
			||||||
                    "applicableUnits": {
 | 
					 | 
				
			||||||
                        "0": {
 | 
					 | 
				
			||||||
                            "human": " meter"
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        "1": {
 | 
					 | 
				
			||||||
                            "human": " feet"
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "roamingRenderings": {
 | 
					 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Is there a (unofficial) website with more informations (e.g. topos)?"
 | 
					                    "question": "Is there a (unofficial) website with more informations (e.g. topos)?"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -588,6 +577,19 @@
 | 
				
			||||||
                    "question": "Is there a speed climbing wall?"
 | 
					                    "question": "Is there a speed climbing wall?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            "units+": {
 | 
				
			||||||
 | 
					                "0": {
 | 
				
			||||||
 | 
					                    "applicableUnits": {
 | 
				
			||||||
 | 
					                        "0": {
 | 
				
			||||||
 | 
					                            "human": " meter"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "1": {
 | 
				
			||||||
 | 
					                            "human": " feet"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "title": "Open Climbing Map"
 | 
					        "title": "Open Climbing Map"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "cycle_highways": {
 | 
					    "cycle_highways": {
 | 
				
			||||||
| 
						 | 
					@ -634,7 +636,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -656,6 +659,7 @@
 | 
				
			||||||
                    "question": "When will this street become a cyclestreet?",
 | 
					                    "question": "When will this street become a cyclestreet?",
 | 
				
			||||||
                    "render": "This street will become a cyclestreet at {cyclestreet:start_date}"
 | 
					                    "render": "This street will become a cyclestreet at {cyclestreet:start_date}"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "A map of cyclestreets",
 | 
					        "shortDescription": "A map of cyclestreets",
 | 
				
			||||||
        "title": "Cyclestreets"
 | 
					        "title": "Cyclestreets"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,7 +225,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Qui est l’exploitant du site ?",
 | 
					                    "question": "Qui est l’exploitant du site ?",
 | 
				
			||||||
                    "render": "Ce site est exploité par {operator}"
 | 
					                    "render": "Ce site est exploité par {operator}"
 | 
				
			||||||
| 
						 | 
					@ -241,6 +242,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "Ce site a-t’il une source d’électricité ?"
 | 
					                    "question": "Ce site a-t’il une source d’électricité ?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Trouver des sites pour passer la nuit avec votre camping-car",
 | 
					        "shortDescription": "Trouver des sites pour passer la nuit avec votre camping-car",
 | 
				
			||||||
        "title": "Campings"
 | 
					        "title": "Campings"
 | 
				
			||||||
| 
						 | 
					@ -439,20 +441,7 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "overrideAll": {
 | 
					        "overrideAll": {
 | 
				
			||||||
            "units+": {
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					 | 
				
			||||||
                    "applicableUnits": {
 | 
					 | 
				
			||||||
                        "0": {
 | 
					 | 
				
			||||||
                            "human": " mètres"
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        "1": {
 | 
					 | 
				
			||||||
                            "human": " pieds"
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "roamingRenderings": {
 | 
					 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Existe-t’il un site avec plus d’informations (ex : topographie) ?"
 | 
					                    "question": "Existe-t’il un site avec plus d’informations (ex : topographie) ?"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -533,6 +522,19 @@
 | 
				
			||||||
                    "question": "Est-il possible d’escalader à la moulinette ?"
 | 
					                    "question": "Est-il possible d’escalader à la moulinette ?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            "units+": {
 | 
				
			||||||
 | 
					                "0": {
 | 
				
			||||||
 | 
					                    "applicableUnits": {
 | 
				
			||||||
 | 
					                        "0": {
 | 
				
			||||||
 | 
					                            "human": " mètres"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "1": {
 | 
				
			||||||
 | 
					                            "human": " pieds"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "title": "Open Climbing Map"
 | 
					        "title": "Open Climbing Map"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "cyclofix": {
 | 
					    "cyclofix": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "1": {
 | 
					                "1": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -64,6 +65,7 @@
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "charging_stations": {
 | 
					    "charging_stations": {
 | 
				
			||||||
        "title": "Stasiun pengisian daya"
 | 
					        "title": "Stasiun pengisian daya"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,7 +225,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Chi gestisce questo luogo?",
 | 
					                    "question": "Chi gestisce questo luogo?",
 | 
				
			||||||
                    "render": "Questo luogo è gestito da {operator}"
 | 
					                    "render": "Questo luogo è gestito da {operator}"
 | 
				
			||||||
| 
						 | 
					@ -241,6 +242,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "Questo luogo fornisce corrente elettrica?"
 | 
					                    "question": "Questo luogo fornisce corrente elettrica?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Trova aree dove passare la notte con il tuo camper",
 | 
					        "shortDescription": "Trova aree dove passare la notte con il tuo camper",
 | 
				
			||||||
        "title": "Aree camper"
 | 
					        "title": "Aree camper"
 | 
				
			||||||
| 
						 | 
					@ -310,7 +312,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "3": {
 | 
					                        "3": {
 | 
				
			||||||
| 
						 | 
					@ -323,6 +326,7 @@
 | 
				
			||||||
                    "render": "Questa strada diventerà una strada ciclabile dal {cyclestreet:start_date}"
 | 
					                    "render": "Questa strada diventerà una strada ciclabile dal {cyclestreet:start_date}"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "cyclofix": {
 | 
					    "cyclofix": {
 | 
				
			||||||
        "description": "Questa mappa offre a chi va in bici una soluzione semplice per trovare tutte le infrastrutture di cui ha bisogno.<br><br>Puoi tracciare la tua posizione esatta (solo su mobile) e selezionare i livelli che ti interessano nell'angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare punti di interesse alla mappa e aggiungere nuove informazioni rispendendo alle domande.<br><br>Tutte le modifiche che apporterai saranno automaticamente salvate nel database mondiale di OpenStreetMap e potranno essere liberamente riutilizzate da tutti e tutte.<br><br>Per maggiori informazioni sul progetto ciclofix, visita <a href='https://cyclofix.osm.be/'>cyclofix.osm.be</a>.",
 | 
					        "description": "Questa mappa offre a chi va in bici una soluzione semplice per trovare tutte le infrastrutture di cui ha bisogno.<br><br>Puoi tracciare la tua posizione esatta (solo su mobile) e selezionare i livelli che ti interessano nell'angolo in basso a sinistra. Puoi anche usare questo strumento per aggiungere o modificare punti di interesse alla mappa e aggiungere nuove informazioni rispendendo alle domande.<br><br>Tutte le modifiche che apporterai saranno automaticamente salvate nel database mondiale di OpenStreetMap e potranno essere liberamente riutilizzate da tutti e tutte.<br><br>Per maggiori informazioni sul progetto ciclofix, visita <a href='https://cyclofix.osm.be/'>cyclofix.osm.be</a>.",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -225,7 +225,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "この店は誰が経営しているんですか?",
 | 
					                    "question": "この店は誰が経営しているんですか?",
 | 
				
			||||||
                    "render": "この場所は{operator}によって運営されます"
 | 
					                    "render": "この場所は{operator}によって運営されます"
 | 
				
			||||||
| 
						 | 
					@ -241,6 +242,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "この場所に電源はありますか?"
 | 
					                    "question": "この場所に電源はありますか?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "キャンパーと夜を共にするキャンプサイトを見つける",
 | 
					        "shortDescription": "キャンパーと夜を共にするキャンプサイトを見つける",
 | 
				
			||||||
        "title": "キャンプサイト"
 | 
					        "title": "キャンプサイト"
 | 
				
			||||||
| 
						 | 
					@ -379,7 +381,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?"
 | 
					                    "question": "もっと情報のある(非公式の)ウェブサイトはありますか(例えば、topos)?"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -468,6 +471,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "スピードクライミングウォールはありますか?"
 | 
					                    "question": "スピードクライミングウォールはありますか?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "title": "登山地図を開く"
 | 
					        "title": "登山地図を開く"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -498,7 +502,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -520,6 +525,7 @@
 | 
				
			||||||
                    "question": "この通りはいつcyclestreetになるんですか?",
 | 
					                    "question": "この通りはいつcyclestreetになるんですか?",
 | 
				
			||||||
                    "render": "この通りは{cyclestreet:start_date}に、cyclestreetになります"
 | 
					                    "render": "この通りは{cyclestreet:start_date}に、cyclestreetになります"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "cyclestreetsの地図",
 | 
					        "shortDescription": "cyclestreetsの地図",
 | 
				
			||||||
        "title": "Cyclestreets"
 | 
					        "title": "Cyclestreets"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -108,7 +108,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "7": {
 | 
					                "7": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -120,6 +121,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "Er buldring mulig her?"
 | 
					                    "question": "Er buldring mulig her?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "title": "Åpent klatrekart"
 | 
					        "title": "Åpent klatrekart"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					@ -136,7 +138,8 @@
 | 
				
			||||||
                "name": "Alle gater"
 | 
					                "name": "Alle gater"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -154,6 +157,7 @@
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "question": "Er denne gaten en sykkelvei?"
 | 
					                    "question": "Er denne gaten en sykkelvei?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Et kart over sykkelveier"
 | 
					        "shortDescription": "Et kart over sykkelveier"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -97,7 +97,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -159,6 +160,7 @@
 | 
				
			||||||
                    "question": "Wat is de naam van dit gebied?",
 | 
					                    "question": "Wat is de naam van dit gebied?",
 | 
				
			||||||
                    "render": "Dit gebied heet {name}"
 | 
					                    "render": "Dit gebied heet {name}"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje",
 | 
					        "shortDescription": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje",
 | 
				
			||||||
        "title": "Breng jouw buurtnatuur in kaart"
 | 
					        "title": "Breng jouw buurtnatuur in kaart"
 | 
				
			||||||
| 
						 | 
					@ -368,20 +370,7 @@
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "overrideAll": {
 | 
					        "overrideAll": {
 | 
				
			||||||
            "units+": {
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					 | 
				
			||||||
                    "applicableUnits": {
 | 
					 | 
				
			||||||
                        "0": {
 | 
					 | 
				
			||||||
                            "human": " meter"
 | 
					 | 
				
			||||||
                        },
 | 
					 | 
				
			||||||
                        "1": {
 | 
					 | 
				
			||||||
                            "human": " voet"
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        "roamingRenderings": {
 | 
					 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
 | 
					                    "question": "Is er een (onofficiële) website met meer informatie (b.v. met topos)?"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -481,6 +470,19 @@
 | 
				
			||||||
                    "question": "Is er een snelklimmuur (speed climbing)?"
 | 
					                    "question": "Is er een snelklimmuur (speed climbing)?"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
 | 
					            "units+": {
 | 
				
			||||||
 | 
					                "0": {
 | 
				
			||||||
 | 
					                    "applicableUnits": {
 | 
				
			||||||
 | 
					                        "0": {
 | 
				
			||||||
 | 
					                            "human": " meter"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "1": {
 | 
				
			||||||
 | 
					                            "human": " voet"
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
        "title": "Open klimkaart"
 | 
					        "title": "Open klimkaart"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "cycle_infra": {
 | 
					    "cycle_infra": {
 | 
				
			||||||
| 
						 | 
					@ -515,7 +517,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "mappings": {
 | 
					                    "mappings": {
 | 
				
			||||||
                        "0": {
 | 
					                        "0": {
 | 
				
			||||||
| 
						 | 
					@ -537,6 +540,7 @@
 | 
				
			||||||
                    "question": "Wanneer wordt deze straat een fietsstraat?",
 | 
					                    "question": "Wanneer wordt deze straat een fietsstraat?",
 | 
				
			||||||
                    "render": "Deze straat wordt fietsstraat op {cyclestreet:start_date}"
 | 
					                    "render": "Deze straat wordt fietsstraat op {cyclestreet:start_date}"
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "shortDescription": "Een kaart met alle gekende fietsstraten",
 | 
					        "shortDescription": "Een kaart met alle gekende fietsstraten",
 | 
				
			||||||
        "title": "Fietsstraten"
 | 
					        "title": "Fietsstraten"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -269,7 +269,8 @@
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "roamingRenderings": {
 | 
					        "overrideAll": {
 | 
				
			||||||
 | 
					            "tagRenderings+": {
 | 
				
			||||||
                "0": {
 | 
					                "0": {
 | 
				
			||||||
                    "question": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?"
 | 
					                    "question": "Есть ли (неофициальный) веб-сайт с более подробной информацией (напр., topos)?"
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
| 
						 | 
					@ -290,6 +291,7 @@
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        "title": "Открытая карта скалолазания"
 | 
					        "title": "Открытая карта скалолазания"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										14
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										14
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							| 
						 | 
					@ -30,7 +30,7 @@
 | 
				
			||||||
        "jspdf": "^2.3.1",
 | 
					        "jspdf": "^2.3.1",
 | 
				
			||||||
        "latlon2country": "^1.1.3",
 | 
					        "latlon2country": "^1.1.3",
 | 
				
			||||||
        "leaflet": "^1.7.1",
 | 
					        "leaflet": "^1.7.1",
 | 
				
			||||||
        "leaflet-providers": "^1.10.2",
 | 
					        "leaflet-providers": "^1.13.0",
 | 
				
			||||||
        "leaflet-simple-map-screenshoter": "^0.4.4",
 | 
					        "leaflet-simple-map-screenshoter": "^0.4.4",
 | 
				
			||||||
        "leaflet.markercluster": "^1.4.1",
 | 
					        "leaflet.markercluster": "^1.4.1",
 | 
				
			||||||
        "libphonenumber": "0.0.10",
 | 
					        "libphonenumber": "0.0.10",
 | 
				
			||||||
| 
						 | 
					@ -10035,9 +10035,9 @@
 | 
				
			||||||
      "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
 | 
					      "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/leaflet-providers": {
 | 
					    "node_modules/leaflet-providers": {
 | 
				
			||||||
      "version": "1.12.0",
 | 
					      "version": "1.13.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.12.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.13.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-pU/mR4B+NbayBGCg5/88dmRq7t1EGiNPhsVGV3yqHuDn594vIwus4CiPVW0RtiKJNKg8Vf1pILAbFl0i+yk+lQ=="
 | 
					      "integrity": "sha512-f/sN5wdgBbVA2jcCYzScIfYNxKdn2wBJP9bu+5cRX9Xj6g8Bt1G9Sr8WgJAt/ckIFIc3LVVxCBNFpSCfTuUElg=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/leaflet-simple-map-screenshoter": {
 | 
					    "node_modules/leaflet-simple-map-screenshoter": {
 | 
				
			||||||
      "version": "0.4.4",
 | 
					      "version": "0.4.4",
 | 
				
			||||||
| 
						 | 
					@ -25965,9 +25965,9 @@
 | 
				
			||||||
      "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
 | 
					      "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "leaflet-providers": {
 | 
					    "leaflet-providers": {
 | 
				
			||||||
      "version": "1.12.0",
 | 
					      "version": "1.13.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.12.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/leaflet-providers/-/leaflet-providers-1.13.0.tgz",
 | 
				
			||||||
      "integrity": "sha512-pU/mR4B+NbayBGCg5/88dmRq7t1EGiNPhsVGV3yqHuDn594vIwus4CiPVW0RtiKJNKg8Vf1pILAbFl0i+yk+lQ=="
 | 
					      "integrity": "sha512-f/sN5wdgBbVA2jcCYzScIfYNxKdn2wBJP9bu+5cRX9Xj6g8Bt1G9Sr8WgJAt/ckIFIc3LVVxCBNFpSCfTuUElg=="
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "leaflet-simple-map-screenshoter": {
 | 
					    "leaflet-simple-map-screenshoter": {
 | 
				
			||||||
      "version": "0.4.4",
 | 
					      "version": "0.4.4",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -77,7 +77,7 @@
 | 
				
			||||||
    "jspdf": "^2.3.1",
 | 
					    "jspdf": "^2.3.1",
 | 
				
			||||||
    "latlon2country": "^1.1.3",
 | 
					    "latlon2country": "^1.1.3",
 | 
				
			||||||
    "leaflet": "^1.7.1",
 | 
					    "leaflet": "^1.7.1",
 | 
				
			||||||
    "leaflet-providers": "^1.10.2",
 | 
					    "leaflet-providers": "^1.13.0",
 | 
				
			||||||
    "leaflet-simple-map-screenshoter": "^0.4.4",
 | 
					    "leaflet-simple-map-screenshoter": "^0.4.4",
 | 
				
			||||||
    "leaflet.markercluster": "^1.4.1",
 | 
					    "leaflet.markercluster": "^1.4.1",
 | 
				
			||||||
    "libphonenumber": "0.0.10",
 | 
					    "libphonenumber": "0.0.10",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										143
									
								
								test/Actors.spec.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								test/Actors.spec.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,143 @@
 | 
				
			||||||
 | 
					import T from "./TestHelper";
 | 
				
			||||||
 | 
					import State from "../State";
 | 
				
			||||||
 | 
					import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
 | 
				
			||||||
 | 
					import SelectedElementTagsUpdater from "../Logic/Actors/SelectedElementTagsUpdater";
 | 
				
			||||||
 | 
					import UserRelatedState from "../Logic/State/UserRelatedState";
 | 
				
			||||||
 | 
					import {Utils} from "../Utils";
 | 
				
			||||||
 | 
					import ScriptUtils from "../scripts/ScriptUtils";
 | 
				
			||||||
 | 
					import SelectedFeatureHandler from "../Logic/Actors/SelectedFeatureHandler";
 | 
				
			||||||
 | 
					import {UIEventSource} from "../Logic/UIEventSource";
 | 
				
			||||||
 | 
					import {ElementStorage} from "../Logic/ElementStorage";
 | 
				
			||||||
 | 
					import Loc from "../Models/Loc";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class ActorsSpec extends T {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const latestTags = {
 | 
				
			||||||
 | 
					            "amenity": "public_bookcase",
 | 
				
			||||||
 | 
					            "books": "children;adults",
 | 
				
			||||||
 | 
					            "capacity": "25",
 | 
				
			||||||
 | 
					            "description": "Deze boekenruilkast vindt je recht tegenover de Pim Pam Poem",
 | 
				
			||||||
 | 
					            "image:0": "https://i.imgur.com/Z8a69UG.jpg",
 | 
				
			||||||
 | 
					            "name": "Stubbekwartier-buurtbibliotheek",
 | 
				
			||||||
 | 
					            "nobrand": "yes",
 | 
				
			||||||
 | 
					            "opening_hours": "24/7",
 | 
				
			||||||
 | 
					            "operator": "Huisbewoner",
 | 
				
			||||||
 | 
					            "public_bookcase:type": "reading_box"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Utils.injectJsonDownloadForTests(
 | 
				
			||||||
 | 
					            "https://www.openstreetmap.org/api/0.6/node/5568693115",
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "version": "0.6",
 | 
				
			||||||
 | 
					                "generator": "CGImap 0.8.5 (1815943 spike-06.openstreetmap.org)",
 | 
				
			||||||
 | 
					                "copyright": "OpenStreetMap and contributors",
 | 
				
			||||||
 | 
					                "attribution": "http://www.openstreetmap.org/copyright",
 | 
				
			||||||
 | 
					                "license": "http://opendatacommons.org/licenses/odbl/1-0/",
 | 
				
			||||||
 | 
					                "elements": [{
 | 
				
			||||||
 | 
					                    "type": "node",
 | 
				
			||||||
 | 
					                    "id": 5568693115,
 | 
				
			||||||
 | 
					                    "lat": 51.2179199,
 | 
				
			||||||
 | 
					                    "lon": 3.2154662,
 | 
				
			||||||
 | 
					                    "timestamp": "2021-08-21T16:22:55Z",
 | 
				
			||||||
 | 
					                    "version": 6,
 | 
				
			||||||
 | 
					                    "changeset": 110034454,
 | 
				
			||||||
 | 
					                    "user": "Pieter Vander Vennet",
 | 
				
			||||||
 | 
					                    "uid": 3818858,
 | 
				
			||||||
 | 
					                    "tags": latestTags
 | 
				
			||||||
 | 
					                }]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super("Actors", [
 | 
				
			||||||
 | 
					            [
 | 
				
			||||||
 | 
					                "download latest version",
 | 
				
			||||||
 | 
					                () => {
 | 
				
			||||||
 | 
					                    const state = new UserRelatedState(AllKnownLayouts.allKnownLayouts.get("bookcases"))
 | 
				
			||||||
 | 
					                    const feature = {
 | 
				
			||||||
 | 
					                        "type": "Feature",
 | 
				
			||||||
 | 
					                        "id": "node/5568693115",
 | 
				
			||||||
 | 
					                        "properties": {
 | 
				
			||||||
 | 
					                            "amenity": "public_bookcase",
 | 
				
			||||||
 | 
					                            "books": "children;adults",
 | 
				
			||||||
 | 
					                            "capacity": "25",
 | 
				
			||||||
 | 
					                            "description": "Deze boekenruilkast vindt je recht tegenover de Pim Pam Poem",
 | 
				
			||||||
 | 
					                            "image:0": "https://i.imgur.com/Z8a69UG.jpg",
 | 
				
			||||||
 | 
					                            "name": "OUTDATED NAME",
 | 
				
			||||||
 | 
					                            "nobrand": "yes",
 | 
				
			||||||
 | 
					                            "opening_hours": "24/7",
 | 
				
			||||||
 | 
					                            "operator": "Huisbewoner",
 | 
				
			||||||
 | 
					                            "public_bookcase:type": "reading_box",
 | 
				
			||||||
 | 
					                            "id": "node/5568693115",
 | 
				
			||||||
 | 
					                            "_lat": "51.2179199",
 | 
				
			||||||
 | 
					                            "_lon": "3.2154662",
 | 
				
			||||||
 | 
					                            "fixme": "SOME FIXME"
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "geometry": {
 | 
				
			||||||
 | 
					                            "type": "Point",
 | 
				
			||||||
 | 
					                            "coordinates": [
 | 
				
			||||||
 | 
					                                3.2154662,
 | 
				
			||||||
 | 
					                                51.2179199
 | 
				
			||||||
 | 
					                            ]
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "bbox": {
 | 
				
			||||||
 | 
					                            "maxLat": 51.2179199,
 | 
				
			||||||
 | 
					                            "maxLon": 3.2154662,
 | 
				
			||||||
 | 
					                            "minLat": 51.2179199,
 | 
				
			||||||
 | 
					                            "minLon": 3.2154662
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        "_lon": 3.2154662,
 | 
				
			||||||
 | 
					                        "_lat": 51.2179199
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    state.allElements.addOrGetElement(feature)
 | 
				
			||||||
 | 
					                    SelectedElementTagsUpdater.installCallback(state)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // THis should trigger a download of the latest feaures and update the tags
 | 
				
			||||||
 | 
					                    // However, this doesn't work with ts-node for some reason
 | 
				
			||||||
 | 
					                    state.selectedElement.setData(feature)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    SelectedElementTagsUpdater.applyUpdate(state, latestTags, feature.properties.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // The name should be updated
 | 
				
			||||||
 | 
					                    T.equals("Stubbekwartier-buurtbibliotheek", feature.properties.name)
 | 
				
			||||||
 | 
					                    // The fixme should be removed
 | 
				
			||||||
 | 
					                    T.equals(undefined, feature.properties.fixme)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                }],
 | 
				
			||||||
 | 
					            ["Hash without selected element should download geojson from OSM-API", async () => {
 | 
				
			||||||
 | 
					                const hash = new UIEventSource("node/5568693115")
 | 
				
			||||||
 | 
					                const selected = new UIEventSource(undefined)
 | 
				
			||||||
 | 
					                const loc = new UIEventSource<Loc>({
 | 
				
			||||||
 | 
					                    lat: 0,
 | 
				
			||||||
 | 
					                    lon: 0,
 | 
				
			||||||
 | 
					                    zoom: 0
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                loc.addCallback(_ => {
 | 
				
			||||||
 | 
					                    T.equals("node/5568693115", selected.data.properties.id)
 | 
				
			||||||
 | 
					                    T.equals(14, loc.data.zoom)
 | 
				
			||||||
 | 
					                    T.equals( 51.2179199, loc.data.lat)
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                new SelectedFeatureHandler(hash, {
 | 
				
			||||||
 | 
					                    selectedElement: selected,
 | 
				
			||||||
 | 
					                    allElements: new ElementStorage(),
 | 
				
			||||||
 | 
					                    featurePipeline: undefined,
 | 
				
			||||||
 | 
					                    locationControl: loc,
 | 
				
			||||||
 | 
					                    layoutToUse: undefined
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,7 @@ import {Utils} from "../Utils";
 | 
				
			||||||
import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec";
 | 
					import TileFreshnessCalculatorSpec from "./TileFreshnessCalculator.spec";
 | 
				
			||||||
import WikidataSpecTest from "./Wikidata.spec.test";
 | 
					import WikidataSpecTest from "./Wikidata.spec.test";
 | 
				
			||||||
import ImageProviderSpec from "./ImageProvider.spec";
 | 
					import ImageProviderSpec from "./ImageProvider.spec";
 | 
				
			||||||
 | 
					import ActorsSpec from "./Actors.spec";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ScriptUtils.fixUtils()
 | 
					ScriptUtils.fixUtils()
 | 
				
			||||||
| 
						 | 
					@ -27,7 +28,8 @@ const allTests = [
 | 
				
			||||||
    new SplitActionSpec(),
 | 
					    new SplitActionSpec(),
 | 
				
			||||||
    new TileFreshnessCalculatorSpec(),
 | 
					    new TileFreshnessCalculatorSpec(),
 | 
				
			||||||
    new WikidataSpecTest(),
 | 
					    new WikidataSpecTest(),
 | 
				
			||||||
    new ImageProviderSpec()
 | 
					    new ImageProviderSpec(),
 | 
				
			||||||
 | 
					    new ActorsSpec()
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Utils.externalDownloadFunction = async (url) => {
 | 
					Utils.externalDownloadFunction = async (url) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@ export default class T {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                test();
 | 
					                test();
 | 
				
			||||||
            } catch (e) {
 | 
					            } catch (e) {
 | 
				
			||||||
 | 
					                console.log("ERROR: ", e, e.stack)
 | 
				
			||||||
                failures.push({testsuite: this.name, name: name, msg: "" + e});
 | 
					                failures.push({testsuite: this.name, name: name, msg: "" + e});
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue