diff --git a/assets/layers/artwork/artwork.json b/assets/layers/artwork/artwork.json index f3310947c..25055f1a3 100644 --- a/assets/layers/artwork/artwork.json +++ b/assets/layers/artwork/artwork.json @@ -187,7 +187,7 @@ } ], "tagRenderings": [ - "images", + "images_no_blur", { "labels": [ "artwork-question" diff --git a/assets/layers/ghost_bike/ghost_bike.json b/assets/layers/ghost_bike/ghost_bike.json index 89b91ec4c..32cc6947b 100644 --- a/assets/layers/ghost_bike/ghost_bike.json +++ b/assets/layers/ghost_bike/ghost_bike.json @@ -142,7 +142,7 @@ ], "tagRenderings": [ "preset_description", - "images", + "images_no_blur", { "question": { "en": "Whom is remembered by this ghost bike?", diff --git a/assets/layers/memorial/memorial.json b/assets/layers/memorial/memorial.json index 96ad225bb..6e4c23254 100644 --- a/assets/layers/memorial/memorial.json +++ b/assets/layers/memorial/memorial.json @@ -91,7 +91,7 @@ } ], "tagRenderings": [ - "images", + "images_no_blur", { "id": "memorial-type", "labels": [ diff --git a/assets/layers/questions/questions.json b/assets/layers/questions/questions.json index 821b05935..bb6cdb40c 100644 --- a/assets/layers/questions/questions.json +++ b/assets/layers/questions/questions.json @@ -19,6 +19,13 @@ "*": "{image_carousel()}{image_upload()}" } }, + { + "id": "images_no_blur", + "description": "Same as `images`, but uploaded request to disable blurring to the panoramax server", + "render": { + "*": "{image_carousel()}{image_upload(,,,true)}" + } + }, { "id": "mapillary", "description": "Shows a button to open Mapillary on this location", diff --git a/package-lock.json b/package-lock.json index 176865fd9..0d7e58762 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,7 +64,7 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.3.6", + "panoramax-js": "^0.3.8", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pg": "^8.11.3", @@ -16002,9 +16002,9 @@ "license": "MIT" }, "node_modules/panoramax-js": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.6.tgz", - "integrity": "sha512-CRdXwh91H6chd1PYptG070ukx+S6IkVaUeQVG91ySevoJoCYOuBT65qkMhRo49X2um1nGcs9UqolW90R57875g==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.8.tgz", + "integrity": "sha512-l8bUi+urcdqLjckGcEyDmYyAYCKjwFXpk/En7gpi5x32Nq2r9sHq7obX3Jbk1F7zH4rmpKbVtjS97cKEZr9zHQ==", "dependencies": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", @@ -32087,9 +32087,9 @@ "version": "1.0.0" }, "panoramax-js": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.6.tgz", - "integrity": "sha512-CRdXwh91H6chd1PYptG070ukx+S6IkVaUeQVG91ySevoJoCYOuBT65qkMhRo49X2um1nGcs9UqolW90R57875g==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.8.tgz", + "integrity": "sha512-l8bUi+urcdqLjckGcEyDmYyAYCKjwFXpk/En7gpi5x32Nq2r9sHq7obX3Jbk1F7zH4rmpKbVtjS97cKEZr9zHQ==", "requires": { "@ogcapi-js/features": "^1.1.1", "@ogcapi-js/shared": "^1.1.1", diff --git a/package.json b/package.json index 5733dc658..3a1802ac9 100644 --- a/package.json +++ b/package.json @@ -208,7 +208,7 @@ "opening_hours": "^3.6.0", "osm-auth": "^2.5.0", "osmtogeojson": "^3.0.0-beta.5", - "panoramax-js": "^0.3.6", + "panoramax-js": "^0.3.8", "panzoom": "^9.4.3", "papaparse": "^5.3.1", "pg": "^8.11.3", diff --git a/src/Logic/ImageProviders/ImageUploadManager.ts b/src/Logic/ImageProviders/ImageUploadManager.ts index 0363f3b21..311226dd3 100644 --- a/src/Logic/ImageProviders/ImageUploadManager.ts +++ b/src/Logic/ImageProviders/ImageUploadManager.ts @@ -81,11 +81,10 @@ export class ImageUploadManager { public canBeUploaded(file: File): true | { error: Translation } { const sizeInBytes = file.size - const self = this if (sizeInBytes > this._uploader.maxFileSizeInMegabytes * 1000000) { const error = Translations.t.image.toBig.Subs({ actual_size: Math.floor(sizeInBytes / 1000000) + "MB", - max_size: self._uploader.maxFileSizeInMegabytes + "MB", + max_size: this._uploader.maxFileSizeInMegabytes + "MB", }) return { error } } @@ -102,7 +101,8 @@ export class ImageUploadManager { public async uploadImageAndApply( file: File, tagsStore: UIEventSource, - targetKey?: string, + targetKey: string, + noblur: boolean, ): Promise { const canBeUploaded = this.canBeUploaded(file) if (canBeUploaded !== true) { @@ -120,6 +120,7 @@ export class ImageUploadManager { author, file, targetKey, + noblur ) if (!uploadResult) { return @@ -139,6 +140,7 @@ export class ImageUploadManager { author: string, blob: File, targetKey: string | undefined, + noblur: boolean ): Promise { this.increaseCountFor(this._uploadStarted, featureId) let key: string @@ -153,12 +155,12 @@ export class ImageUploadManager { location = GeoOperations.centerpointCoordinates(feature) } try { - ;({ key, value, absoluteUrl } = await this._uploader.uploadImage(blob, location, author)) + ({ key, value, absoluteUrl } = await this._uploader.uploadImage(blob, location, author, noblur)) } catch (e) { this.increaseCountFor(this._uploadRetried, featureId) console.error("Could not upload image, trying again:", e) try { - ;({ key, value , absoluteUrl} = await this._uploader.uploadImage(blob, location, author)) + ({ key, value , absoluteUrl} = await this._uploader.uploadImage(blob, location, author, noblur)) this.increaseCountFor(this._uploadRetriedSuccess, featureId) } catch (e) { console.error("Could again not upload image due to", e) diff --git a/src/Logic/ImageProviders/ImageUploader.ts b/src/Logic/ImageProviders/ImageUploader.ts index bd49baa48..c178a6f3f 100644 --- a/src/Logic/ImageProviders/ImageUploader.ts +++ b/src/Logic/ImageProviders/ImageUploader.ts @@ -7,7 +7,8 @@ export interface ImageUploader { uploadImage( blob: File, currentGps: [number,number], - author: string + author: string, + noblur: boolean ): Promise } diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index c05da71b3..96112dce6 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -167,12 +167,12 @@ export class PanoramaxUploader implements ImageUploader { this._panoramax = new AuthorizedPanoramax(url, token) } - async uploadImage(blob: File, currentGps: [number, number], author: string): Promise<{ + async uploadImage(blob: File, currentGps: [number, number], author: string, noblur: boolean = false): Promise<{ key: string; value: string; absoluteUrl: string }> { - + // https://panoramax.openstreetmap.fr/api/docs/swagger#/ const tags = await ExifReader.load(blob) const hasDate = tags.DateTime !== undefined const hasGPS = tags.GPSLatitude !== undefined && tags.GPSLongitude !== undefined @@ -184,6 +184,7 @@ export class PanoramaxUploader implements ImageUploader { const img = await p.addImage(blob, defaultSequence, { lat: !hasGPS ? lat : undefined, lon: !hasGPS ? lon : undefined, + isBlurred: noblur, datetime: !hasDate ? new Date().toISOString() : undefined, exifOverride: { Artist: author, diff --git a/src/UI/Image/UploadImage.svelte b/src/UI/Image/UploadImage.svelte index ff7d4581c..1f5c51d8a 100644 --- a/src/UI/Image/UploadImage.svelte +++ b/src/UI/Image/UploadImage.svelte @@ -4,8 +4,8 @@ */ import type { SpecialVisualizationState } from "../SpecialVisualization" - import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource" - import type { OsmId, OsmTags } from "../../Models/OsmFeature" + import { UIEventSource } from "../../Logic/UIEventSource" + import type { OsmTags } from "../../Models/OsmFeature" import LoginToggle from "../Base/LoginToggle.svelte" import Translations from "../i18n/Translations" import Tr from "../Base/Tr.svelte" @@ -22,6 +22,7 @@ export let tags: UIEventSource export let targetKey: string = undefined export let layer: LayerConfig + export let noBlur: boolean = false /** * Image to show in the button * NOT the image to upload! @@ -47,22 +48,22 @@ continue } - if(layer?.id === "note"){ + if (layer?.id === "note") { const uploadResult = await state?.imageUploadManager.uploadImageWithLicense(tags.data.id, state.osmConnection.userDetails.data?.name ?? "Anonymous", - file, "image") - if(!uploadResult){ + file, "image", noBlur) + if (!uploadResult) { return } const url = uploadResult.absoluteUrl await state.osmConnection.addCommentToNote(tags.data.id, url) NoteCommentElement.addCommentTo(url, >tags, { - osmConnection: state.osmConnection, + osmConnection: state.osmConnection }) return } - await state?.imageUploadManager.uploadImageAndApply(file, tags, targetKey) + await state?.imageUploadManager.uploadImageAndApply(file, tags, targetKey, noBlur) } catch (e) { console.error(e) state.reportError(e, "Could not upload image") @@ -96,12 +97,21 @@ {#if labelText} {labelText} {:else} - +
+ + + {#if noBlur} + + Faces will not be blurred + + {/if} +
{/if} +
- +
diff --git a/src/UI/SpecialVisualizations.ts b/src/UI/SpecialVisualizations.ts index ee038b399..f79c117a3 100644 --- a/src/UI/SpecialVisualizations.ts +++ b/src/UI/SpecialVisualizations.ts @@ -104,13 +104,13 @@ class NearbyImageVis implements SpecialVisualization { { name: "mode", defaultValue: "closed", - doc: "Either `open` or `closed`. If `open`, then the image carousel will always be shown", + doc: "Either `open` or `closed`. If `open`, then the image carousel will always be shown" }, { name: "readonly", required: false, - doc: "If 'readonly', will not show the 'link'-button", - }, + doc: "If 'readonly', will not show the 'link'-button" + } ] docs = "A component showing nearby images loaded from various online services such as Mapillary. In edit mode and when used on a feature, the user can select an image to add to the feature" @@ -122,7 +122,7 @@ class NearbyImageVis implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): SvelteUIElement { const isOpen = args[0] === "open" const readonly = args[1] === "readonly" @@ -134,7 +134,7 @@ class NearbyImageVis implements SpecialVisualization { lat, feature, layer, - linkable: !readonly, + linkable: !readonly }) } } @@ -148,13 +148,13 @@ class StealViz implements SpecialVisualization { { name: "featureId", doc: "The key of the attribute which contains the id of the feature from which to use the tags", - required: true, + required: true }, { name: "tagRenderingId", doc: "The layer-id and tagRenderingId to render. Can be multiple value if ';'-separated (in which case every value must also contain the layerId, e.g. `layerId.tagRendering0; layerId.tagRendering1`). Note: this can cause layer injection", - required: true, - }, + required: true + } ] needsUrls = [] svelteBased = true @@ -188,8 +188,8 @@ class StealViz implements SpecialVisualization { tags: otherTags, selectedElement: otherFeature, state, - layer, - }), + layer + }) ) } if (elements.length === 1) { @@ -197,8 +197,8 @@ class StealViz implements SpecialVisualization { } return new Combine(elements).SetClass("flex flex-col") }, - [state.indexedFeatures.featuresById], - ), + [state.indexedFeatures.featuresById] + ) ) } @@ -221,40 +221,40 @@ class CloseNoteViz implements SpecialVisualization { { name: "text", doc: "Text to show on this button", - required: true, + required: true }, { name: "icon", doc: "Icon to show", - defaultValue: "checkmark.svg", + defaultValue: "checkmark.svg" }, { name: "idkey", doc: "The property name where the ID of the note to close can be found", - defaultValue: "id", + defaultValue: "id" }, { name: "comment", - doc: "Text to add onto the note when closing", + doc: "Text to add onto the note when closing" }, { name: "minZoom", - doc: "If set, only show the closenote button if zoomed in enough", + doc: "If set, only show the closenote button if zoomed in enough" }, { name: "zoomButton", - doc: "Text to show if not zoomed in enough", - }, + doc: "Text to show if not zoomed in enough" + } ] public constr( state: SpecialVisualizationState, tags: UIEventSource>, - args: string[], + args: string[] ): SvelteUIElement { const { text, icon, idkey, comment, minZoom, zoomButton } = Utils.ParseVisArgs( this.args, - args, + args ) return new SvelteUIElement(CloseNoteButton, { @@ -265,7 +265,7 @@ class CloseNoteViz implements SpecialVisualization { message: comment, text: Translations.T(text), minzoom: minZoom, - zoomMoreMessage: zoomButton, + zoomMoreMessage: zoomButton }) } } @@ -281,12 +281,12 @@ export class QuestionViz implements SpecialVisualization { args = [ { name: "labels", - doc: "One or more ';'-separated labels. If these are given, only questions with these labels will be given. Use `unlabeled` for all questions that don't have an explicit label. If none given, all questions will be shown", + doc: "One or more ';'-separated labels. If these are given, only questions with these labels will be given. Use `unlabeled` for all questions that don't have an explicit label. If none given, all questions will be shown" }, { name: "blacklisted-labels", - doc: "One or more ';'-separated labels of questions which should _not_ be included", - }, + doc: "One or more ';'-separated labels of questions which should _not_ be included" + } ] svelteBased = true @@ -295,7 +295,7 @@ export class QuestionViz implements SpecialVisualization { tags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): SvelteUIElement { const labels = args[0] ?.split(";") @@ -311,7 +311,7 @@ export class QuestionViz implements SpecialVisualization { selectedElement: feature, state, onlyForLabels: labels, - notForLabels: blacklist, + notForLabels: blacklist }).SetClass("w-full") } } @@ -327,7 +327,7 @@ export default class SpecialVisualizations { for (const specialVisualization of SpecialVisualizations.specialVisualizations) { SpecialVisualizations.specialVisualisationsDict.set( specialVisualization.funcName, - specialVisualization, + specialVisualization ) } } @@ -354,28 +354,28 @@ export default class SpecialVisualizations { defaultArg = "_empty string_" } return [arg.name, defaultArg, arg.doc] - }), + }) ) : undefined, "#### Example usage of " + viz.funcName, - "" + example + "", + "" + example + "" ].join("\n\n") } public static constructSpecification( template: string, - extraMappings: SpecialVisualization[] = [], + extraMappings: SpecialVisualization[] = [] ): RenderingSpecification[] { return SpecialVisualisationUtils.constructSpecification( template, SpecialVisualizations.specialVisualisationsDict, - extraMappings, + extraMappings ) } public static HelpMessage(): string { const helpTexts: string[] = SpecialVisualizations.specialVisualizations.map((viz) => - SpecialVisualizations.DocumentationFor(viz), + SpecialVisualizations.DocumentationFor(viz) ) const firstPart = new Combine([ @@ -394,24 +394,24 @@ export default class SpecialVisualizations { argname: "some_arg", message: { en: "some other really long message", - nl: "een boodschap in een andere taal", + nl: "een boodschap in een andere taal" }, - other_arg_name: "more args", + other_arg_name: "more args" }, before: { en: "Some text to prefix before the special element (e.g. a title)", - nl: "Een tekst om voor het element te zetten (bv. een titel)", + nl: "Een tekst om voor het element te zetten (bv. een titel)" }, after: { - en: "Some text to put after the element, e.g. a footer", - }, - }, + en: "Some text to put after the element, e.g. a footer" + } + } }, null, - " ", - ), + " " + ) ).SetClass("code"), - "In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)", + "In other words: use `{ \"before\": ..., \"after\": ..., \"special\": {\"type\": ..., \"argname\": ...argvalue...}`. The args are in the `special` block; an argvalue can be a string, a translation or another value. (Refer to class `RewriteSpecial` in case of problems)" ]) .SetClass("flex flex-col") .AsMarkdown() @@ -431,9 +431,9 @@ export default class SpecialVisualizations { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(AddNewPoint, { state, - coordinate: { lon, lat }, + coordinate: { lon, lat } }).SetClass("w-full h-full overflow-auto") - }, + } }, { funcName: "language_picker", @@ -449,12 +449,12 @@ export default class SpecialVisualizations { assignTo: state.userRelatedState.language, availableLanguages: languages, preferredLanguages: state.osmConnection.userDetails.map( - (ud) => ud.languages, - ), + (ud) => ud.languages + ) }) - }), + }) ) - }, + } }, { funcName: "logout", @@ -464,7 +464,7 @@ export default class SpecialVisualizations { constr(state: SpecialVisualizationState): BaseUIElement { return new SvelteUIElement(LogoutButton, { osmConnection: state.osmConnection }) - }, + } }, new HistogramViz(), new StealViz(), @@ -476,13 +476,13 @@ export default class SpecialVisualizations { { doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close", name: "zoomlevel", - defaultValue: "18", + defaultValue: "18" }, { doc: "(Matches all resting arguments) This argument should be the key of a property of the feature. The corresponding value is interpreted as either the id or the a list of ID's. The features with these ID's will be shown on this minimap. (Note: if the key is 'id', list interpration is disabled)", name: "idKey", - defaultValue: "id", - }, + defaultValue: "id" + } ], example: "`{minimap()}`, `{minimap(17, id, _list_of_embedded_feature_ids_calculated_by_calculated_tag):height:10rem; border: 2px solid black}`", @@ -491,10 +491,10 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], - feature: Feature, + feature: Feature ): SvelteUIElement { return new SvelteUIElement(MinimapViz, { state, args, feature, tagSource }) - }, + } }, { funcName: "split_button", @@ -503,7 +503,7 @@ export default class SpecialVisualizations { constr( state: SpecialVisualizationState, - tagSource: UIEventSource>, + tagSource: UIEventSource> ): BaseUIElement { return new VariableUiElement( tagSource @@ -513,9 +513,9 @@ export default class SpecialVisualizations { return new SvelteUIElement(SplitRoadWizard, { id, state }) } return undefined - }), + }) ) - }, + } }, { funcName: "move_button", @@ -527,7 +527,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { if (feature.geometry.type !== "Point") { return undefined @@ -536,9 +536,9 @@ export default class SpecialVisualizations { return new SvelteUIElement(MoveWizard, { state, featureToMove: feature, - layer, + layer }) - }, + } }, { funcName: "delete_button", @@ -550,7 +550,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { if (!layer.deletion) { return undefined @@ -560,9 +560,9 @@ export default class SpecialVisualizations { deleteConfig: layer.deletion, state, feature, - layer, + layer }).SetClass("p-0 m-0") - }, + } }, new ShareLinkViz(), new ExportAsGpxViz(), @@ -578,14 +578,14 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], - feature: Feature, + feature: Feature ): BaseUIElement { const [lon, lat] = GeoOperations.centerpointCoordinates(feature) return new SvelteUIElement(CreateNewNote, { state, - coordinate: new UIEventSource({ lon, lat }), + coordinate: new UIEventSource({ lon, lat }) }) - }, + } }, new CloseNoteViz(), new PlantNetDetectionViz(), @@ -604,8 +604,8 @@ export default class SpecialVisualizations { { name: "keyToShowWikipediaFor", doc: "Use the wikidata entry from this key to show the wikipedia article for. Multiple keys can be given (separated by ';'), in which case the first matching value is used", - defaultValue: "wikidata;wikipedia", - }, + defaultValue: "wikidata;wikipedia" + } ], needsUrls: [...Wikidata.neededUrls, ...Wikipedia.neededUrls], @@ -618,9 +618,9 @@ export default class SpecialVisualizations { return tags[key]?.split(";")?.map((id) => id.trim()) ?? [] }) return new SvelteUIElement(WikipediaPanel, { - wikiIds, + wikiIds }) - }, + } }, { funcName: "wikidata_label", @@ -629,8 +629,8 @@ export default class SpecialVisualizations { { name: "keyToShowWikidataFor", doc: "Use the wikidata entry from this key to show the label", - defaultValue: "wikidata", - }, + defaultValue: "wikidata" + } ], needsUrls: Wikidata.neededUrls, example: @@ -641,7 +641,7 @@ export default class SpecialVisualizations { .map((tags) => tags[args[0]]) .map((wikidata) => { wikidata = Utils.NoEmpty( - wikidata?.split(";")?.map((wd) => wd.trim()) ?? [], + wikidata?.split(";")?.map((wd) => wd.trim()) ?? [] )[0] const entry = Wikidata.LoadWikidataEntry(wikidata) return new VariableUiElement( @@ -651,10 +651,10 @@ export default class SpecialVisualizations { } const response = e["success"] return Translation.fromMap(response.labels) - }), + }) ) - }), - ), + }) + ) }, new MapillaryLinkVis(), new LanguageElement(), @@ -667,8 +667,8 @@ export default class SpecialVisualizations { tags: UIEventSource>, _, __, - layer: LayerConfig, - ) => new SvelteUIElement(AllTagsPanel, { tags, layer }), + layer: LayerConfig + ) => new SvelteUIElement(AllTagsPanel, { tags, layer }) }, { funcName: "image_carousel", @@ -677,8 +677,8 @@ export default class SpecialVisualizations { { name: "image_key", defaultValue: AllImageProviders.defaultKeys.join(","), - doc: "The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... Multiple values are allowed if ';'-separated ", - }, + doc: "The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... Multiple values are allowed if ';'-separated " + } ], needsUrls: AllImageProviders.apiUrls, constr: (state, tags, args) => { @@ -689,9 +689,9 @@ export default class SpecialVisualizations { return new ImageCarousel( AllImageProviders.LoadImagesFor(tags, imagePrefixes), tags, - state, + state ) - }, + } }, { funcName: "image_upload", @@ -701,24 +701,31 @@ export default class SpecialVisualizations { { name: "image-key", doc: "Image tag to add the URL to (or image-tag:0, image-tag:1 when multiple images are added)", - required: false, + required: false }, { name: "label", doc: "The text to show on the button", - required: false, + required: false }, + { + name: "disable_blur", + doc: "If set to 'true' or 'yes', then face blurring will be disabled. To be used sparingly", + required: false + } ], constr: (state, tags, args) => { const targetKey = args[0] === "" ? undefined : args[0] + const noBlur = args[3]?.toLowerCase()?.trim() return new SvelteUIElement(UploadImage, { state, tags, targetKey, labelText: args[1], image: args[2], + noBlur: noBlur === "true" || noBlur === "yes" }) - }, + } }, { funcName: "rating", @@ -728,12 +735,12 @@ export default class SpecialVisualizations { { name: "subjectKey", defaultValue: "name", - doc: "The key to use to determine the subject. If the value is specified, the subject will be tags[subjectKey] and will use this to filter the reviews.", + doc: "The key to use to determine the subject. If the value is specified, the subject will be tags[subjectKey] and will use this to filter the reviews." }, { name: "fallback", - doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value", - }, + doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value" + } ], constr: (state, tags, args, feature) => { const nameKey = args[0] ?? "name" @@ -744,14 +751,14 @@ export default class SpecialVisualizations { state.userRelatedState.mangroveIdentity, { nameKey: nameKey, - fallbackName, + fallbackName }, - state.featureSwitchIsTesting, + state.featureSwitchIsTesting ) return new SvelteUIElement(StarsBarIcon, { - score: reviews.average, + score: reviews.average }) - }, + } }, { @@ -762,12 +769,12 @@ export default class SpecialVisualizations { { name: "subjectKey", defaultValue: "name", - doc: "The key to use to determine the subject. If specified, the subject will be tags[subjectKey]", + doc: "The key to use to determine the subject. If specified, the subject will be tags[subjectKey]" }, { name: "fallback", - doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value", - }, + doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value" + } ], constr: (state, tags, args, feature, layer) => { const nameKey = args[0] ?? "name" @@ -778,12 +785,12 @@ export default class SpecialVisualizations { state.userRelatedState?.mangroveIdentity, { nameKey: nameKey, - fallbackName, + fallbackName }, - state.featureSwitchIsTesting, + state.featureSwitchIsTesting ) return new SvelteUIElement(ReviewForm, { reviews, state, tags, feature, layer }) - }, + } }, { funcName: "list_reviews", @@ -793,12 +800,12 @@ export default class SpecialVisualizations { { name: "subjectKey", defaultValue: "name", - doc: "The key to use to determine the subject. If specified, the subject will be tags[subjectKey]", + doc: "The key to use to determine the subject. If specified, the subject will be tags[subjectKey]" }, { name: "fallback", - doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value", - }, + doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value" + } ], constr: (state, tags, args, feature, layer) => { const nameKey = args[0] ?? "name" @@ -809,12 +816,12 @@ export default class SpecialVisualizations { state.userRelatedState?.mangroveIdentity, { nameKey: nameKey, - fallbackName, + fallbackName }, - state.featureSwitchIsTesting, + state.featureSwitchIsTesting ) return new SvelteUIElement(AllReviews, { reviews, state, tags, feature, layer }) - }, + } }, { funcName: "reviews", @@ -825,19 +832,19 @@ export default class SpecialVisualizations { { name: "subjectKey", defaultValue: "name", - doc: "The key to use to determine the subject. If specified, the subject will be tags[subjectKey]", + doc: "The key to use to determine the subject. If specified, the subject will be tags[subjectKey]" }, { name: "fallback", - doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value", - }, + doc: "The identifier to use, if tags[subjectKey] as specified above is not available. This is effectively a fallback value" + } ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new Combine([ SpecialVisualizations.specialVisualisationsDict["create_review"].constr( @@ -845,17 +852,17 @@ export default class SpecialVisualizations { tagSource, args, feature, - layer, + layer ), SpecialVisualizations.specialVisualisationsDict["list_reviews"].constr( state, tagSource, args, feature, - layer, - ), + layer + ) ]) - }, + } }, { funcName: "import_mangrove_key", @@ -863,18 +870,18 @@ export default class SpecialVisualizations { args: [ { name: "text", - doc: "The text that is shown on the button", - }, + doc: "The text that is shown on the button" + } ], needsUrls: [], constr( state: SpecialVisualizationState, _: UIEventSource>, - argument: string[], + argument: string[] ): BaseUIElement { const [text] = argument return new SvelteUIElement(ImportReviewIdentity, { state, text }) - }, + } }, { funcName: "opening_hours_table", @@ -883,18 +890,18 @@ export default class SpecialVisualizations { { name: "key", defaultValue: "opening_hours", - doc: "The tagkey from which the table is constructed.", + doc: "The tagkey from which the table is constructed." }, { name: "prefix", defaultValue: "", - doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__", + doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" }, { name: "postfix", defaultValue: "", - doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__", - }, + doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" + } ], needsUrls: [Constants.countryCoderEndpoint], example: @@ -902,7 +909,7 @@ export default class SpecialVisualizations { constr: (state, tagSource: UIEventSource, args) => { const [key, prefix, postfix] = args return new OpeningHoursVisualization(tagSource, key, prefix, postfix) - }, + } }, { funcName: "opening_hours_state", @@ -911,23 +918,23 @@ export default class SpecialVisualizations { { name: "key", defaultValue: "opening_hours", - doc: "The tagkey from which the opening hours are read.", + doc: "The tagkey from which the opening hours are read." }, { name: "prefix", defaultValue: "", - doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__", + doc: "Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" }, { name: "postfix", defaultValue: "", - doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__", - }, + doc: "Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" + } ], constr( state: SpecialVisualizationState, tags: UIEventSource>, - args: string[], + args: string[] ): SvelteUIElement { const keyToUse = args[0] const prefix = args[1] @@ -937,9 +944,9 @@ export default class SpecialVisualizations { keyToUse, tags, prefix, - postfix, + postfix }) - }, + } }, { funcName: "canonical", @@ -951,8 +958,8 @@ export default class SpecialVisualizations { { name: "key", doc: "The key of the tag to give the canonical text for", - required: true, - }, + required: true + } ], constr: (state, tagSource, args) => { const key = args[0] @@ -964,19 +971,19 @@ export default class SpecialVisualizations { return undefined } const allUnits: Unit[] = [].concat( - ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []), + ...(state?.layout?.layers?.map((lyr) => lyr.units) ?? []) ) const unit = allUnits.filter((unit) => - unit.isApplicableToKey(key), + unit.isApplicableToKey(key) )[0] if (unit === undefined) { return value } const getCountry = () => tagSource.data._country return unit.asHumanLongValue(value, getCountry) - }), + }) ) - }, + } }, { funcName: "export_as_geojson", @@ -990,8 +997,8 @@ export default class SpecialVisualizations { new SvelteUIElement(ArrowDownTray), new Combine([ t.downloadFeatureAsGeojson.SetClass("font-bold text-lg"), - t.downloadGeoJsonHelper.SetClass("subtle"), - ]).SetClass("flex flex-col"), + t.downloadGeoJsonHelper.SetClass("subtle") + ]).SetClass("flex flex-col") ) .onClick(() => { console.log("Exporting as Geojson") @@ -1003,12 +1010,12 @@ export default class SpecialVisualizations { data, title + "_mapcomplete_export.geojson", { - mimetype: "application/vnd.geo+json", - }, + mimetype: "application/vnd.geo+json" + } ) }) .SetClass("w-full") - }, + } }, { funcName: "open_in_iD", @@ -1018,9 +1025,9 @@ export default class SpecialVisualizations { constr: (state, feature) => { return new SvelteUIElement(OpenIdEditor, { mapProperties: state.mapProperties, - objectId: feature.data.id, + objectId: feature.data.id }) - }, + } }, { funcName: "open_in_josm", @@ -1030,7 +1037,7 @@ export default class SpecialVisualizations { constr: (state) => { return new SvelteUIElement(OpenJosm, { state }) - }, + } }, { funcName: "clear_location_history", @@ -1040,12 +1047,12 @@ export default class SpecialVisualizations { constr: (state) => { return new SubtleButton( new SvelteUIElement(Trash).SetClass("h-6"), - Translations.t.general.removeLocationHistory, + Translations.t.general.removeLocationHistory ).onClick(() => { state.historicalUserLocations.features.setData([]) state.selectedElement.setData(undefined) }) - }, + } }, { funcName: "visualize_note_comments", @@ -1054,13 +1061,13 @@ export default class SpecialVisualizations { { name: "commentsKey", doc: "The property name of the comments, which should be stringified json", - defaultValue: "comments", + defaultValue: "comments" }, { name: "start", doc: "Drop the first 'start' comments", - defaultValue: "0", - }, + defaultValue: "0" + } ], needsUrls: [Constants.osmAuthConfig.url], constr: (state, tags, args) => @@ -1078,11 +1085,11 @@ export default class SpecialVisualizations { .filter((c) => c.text !== "") .map( (comment) => - new SvelteUIElement(NoteCommentElement, { comment, state }), - ), + new SvelteUIElement(NoteCommentElement, { comment, state }) + ) ).SetClass("flex flex-col") - }), - ), + }) + ) }, { funcName: "add_image_to_note", @@ -1091,8 +1098,8 @@ export default class SpecialVisualizations { { name: "Id-key", doc: "The property name where the ID of the note to close can be found", - defaultValue: "id", - }, + defaultValue: "id" + } ], needsUrls: [Imgur.apiUrl, ...Imgur.supportingUrls], @@ -1100,7 +1107,7 @@ export default class SpecialVisualizations { const id = tags.data[args[0] ?? "id"] tags = state.featureProperties.getStore(id) return new SvelteUIElement(UploadImage, { state, tags, layer }) - }, + } }, { funcName: "title", @@ -1114,7 +1121,7 @@ export default class SpecialVisualizations { tagsSource: UIEventSource>, _: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ) => new VariableUiElement( tagsSource.map((tags) => { @@ -1130,12 +1137,12 @@ export default class SpecialVisualizations { tags: tagsSource, state, feature, - layer, + layer }) .SetClass("px-1") .setSpan() - }), - ), + }) + ) }, { funcName: "maproulette_task", @@ -1150,8 +1157,8 @@ export default class SpecialVisualizations { const challenge = Stores.FromPromise( Utils.downloadJsonCached( `${Maproulette.defaultEndpoint}/challenge/${parentId}`, - 24 * 60 * 60 * 1000, - ), + 24 * 60 * 60 * 1000 + ) ) return new VariableUiElement( @@ -1176,10 +1183,10 @@ export default class SpecialVisualizations { } else { return [title, new List(listItems)] } - }), + }) ) }, - docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign.", + docs: "Fetches the metadata of MapRoulette campaign that this task is part of and shows those details (namely `title`, `description` and `instruction`).\n\nThis reads the property `mr_challengeId` to detect the parent campaign." }, { funcName: "maproulette_set_status", @@ -1206,32 +1213,32 @@ export default class SpecialVisualizations { args: [ { name: "message", - doc: "A message to show to the user", + doc: "A message to show to the user" }, { name: "image", doc: "Image to show", - defaultValue: "confirm", + defaultValue: "confirm" }, { name: "message_confirm", - doc: "What to show when the task is closed, either by the user or was already closed.", + doc: "What to show when the task is closed, either by the user or was already closed." }, { name: "status", doc: "A statuscode to apply when the button is clicked. 1 = `close`, 2 = `false_positive`, 3 = `skip`, 4 = `deleted`, 5 = `already fixed` (on the map, e.g. for duplicates), 6 = `too hard`", - defaultValue: "1", + defaultValue: "1" }, { name: "maproulette_id", doc: "The property name containing the maproulette id", - defaultValue: "mr_taskId", + defaultValue: "mr_taskId" }, { name: "ask_feedback", doc: "If not an empty string, this will be used as question to ask some additional feedback. A text field will be added", - defaultValue: "", - }, + defaultValue: "" + } ], constr: (state, tagsSource, args) => { @@ -1241,7 +1248,7 @@ export default class SpecialVisualizations { message_closed, statusToSet, maproulette_id_key, - askFeedback, + askFeedback ] = args if (image === "") { image = "confirm" @@ -1258,9 +1265,9 @@ export default class SpecialVisualizations { message_closed, statusToSet, maproulette_id_key, - askFeedback, + askFeedback }) - }, + } }, { funcName: "statistics", @@ -1274,7 +1281,7 @@ export default class SpecialVisualizations { (l) => l.name !== null && l.title && - state.perLayer.get(l.id) !== undefined, + state.perLayer.get(l.id) !== undefined ) .map( (l) => { @@ -1284,10 +1291,10 @@ export default class SpecialVisualizations { const fsBboxed = new BBoxFeatureSourceForLayer(fs, bbox) return new StatisticsPanel(fsBboxed) }, - [state.mapProperties.bounds], - ), + [state.mapProperties.bounds] + ) ) - }, + } }, { funcName: "send_email", @@ -1296,29 +1303,29 @@ export default class SpecialVisualizations { { name: "to", doc: "Who to send the email to?", - required: true, + required: true }, { name: "subject", doc: "The subject of the email", - required: true, + required: true }, { name: "body", doc: "The text in the email", - required: true, + required: true }, { name: "button_text", doc: "The text shown on the button in the UI", - required: true, - }, + required: true + } ], constr(__, tags, args) { return new SvelteUIElement(SendEmail, { args, tags }) - }, + } }, { funcName: "link", @@ -1327,35 +1334,35 @@ export default class SpecialVisualizations { { name: "text", doc: "Text to be shown", - required: true, + required: true }, { name: "href", doc: "The URL to link to. Note that this will be URI-encoded before ", - required: true, + required: true }, { name: "class", - doc: "CSS-classes to add to the element", + doc: "CSS-classes to add to the element" }, { name: "download", - doc: "Expects a string which denotes the filename to download the contents of `href` into. If set, this link will act as a download-button.", + doc: "Expects a string which denotes the filename to download the contents of `href` into. If set, this link will act as a download-button." }, { name: "arialabel", - doc: "If set, this text will be used as aria-label", + doc: "If set, this text will be used as aria-label" }, { name: "icon", - doc: "If set, show this icon next to the link. You might want to combine this with `class: button`", - }, + doc: "If set, show this icon next to the link. You might want to combine this with `class: button`" + } ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[], + args: string[] ): SvelteUIElement { let [text, href, classnames, download, ariaLabel, icon] = args if (download === "") { @@ -1371,9 +1378,9 @@ export default class SpecialVisualizations { download: tagSource.map((tags) => Utils.SubstituteKeys(download, tags)), ariaLabel: tagSource.map((tags) => Utils.SubstituteKeys(ariaLabel, tags)), newTab: new ImmutableStore(newTab), - icon: tagSource.map((tags) => Utils.SubstituteKeys(icon, tags)), + icon: tagSource.map((tags) => Utils.SubstituteKeys(icon, tags)) }).setSpan() - }, + } }, { funcName: "multi", @@ -1387,37 +1394,37 @@ export default class SpecialVisualizations { type: "multi", key: "_doors_from_building_properties", tagrendering: { - en: "The building containing this feature has a door of width {entrance:width}", - }, - }, - }, + en: "The building containing this feature has a door of width {entrance:width}" + } + } + } }, null, - " ", + " " ) + "\n```", args: [ { name: "key", doc: "The property to read and to interpret as a list of properties", - required: true, + required: true }, { name: "tagrendering", doc: "An entire tagRenderingConfig", - required: true, + required: true }, { name: "classes", - doc: "CSS-classes to apply on every individual item. Seperated by `space`", - }, + doc: "CSS-classes to apply on every individual item. Seperated by `space`" + } ], constr( state: SpecialVisualizationState, featureTags: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ) { const [key, tr, classesRaw] = args let classes = classesRaw ?? "" @@ -1435,7 +1442,7 @@ export default class SpecialVisualizations { "Could not create a special visualization for multi(", args.join(", ") + ")", "no properties found for object", - feature.properties.id, + feature.properties.id ) return undefined } @@ -1446,14 +1453,14 @@ export default class SpecialVisualizations { tags: new ImmutableStore(property), state, feature, - layer, + layer }).SetClass(classes) elements.push(subsTr) } return elements - }), + }) ) - }, + } }, { funcName: "translated", @@ -1463,15 +1470,15 @@ export default class SpecialVisualizations { { name: "key", doc: "The attribute to interpret as json", - defaultValue: "value", - }, + defaultValue: "value" + } ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new VariableUiElement( tagSource.map((tags) => { @@ -1483,9 +1490,9 @@ export default class SpecialVisualizations { console.error("Cannot create a translation for", v, "due to", e) return JSON.stringify(v) } - }), + }) ) - }, + } }, { funcName: "fediverse_link", @@ -1494,8 +1501,8 @@ export default class SpecialVisualizations { { name: "key", doc: "The attribute-name containing the link", - required: true, - }, + required: true + } ], constr( @@ -1503,7 +1510,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const key = argument[0] const validator = new FediverseValidator() @@ -1513,13 +1520,13 @@ export default class SpecialVisualizations { .map((fediAccount) => { fediAccount = validator.reformat(fediAccount) const [_, username, host] = fediAccount.match( - FediverseValidator.usernameAtServer, + FediverseValidator.usernameAtServer ) const normalLink = new SvelteUIElement(Link, { text: fediAccount, href: "https://" + host + "/@" + username, - newTab: true, + newTab: true }) const loggedInContributorMastodon = @@ -1528,7 +1535,7 @@ export default class SpecialVisualizations { ] console.log( "LoggedinContributorMastodon", - loggedInContributorMastodon, + loggedInContributorMastodon ) if (!loggedInContributorMastodon) { return normalLink @@ -1541,12 +1548,12 @@ export default class SpecialVisualizations { new SvelteUIElement(Link, { href: homeHost + "/" + fediAccount, text: Translations.t.validation.fediverse.onYourServer, - newTab: true, - }).SetClass("button"), + newTab: true + }).SetClass("button") ]) - }), + }) ) - }, + } }, { funcName: "braced", @@ -1556,18 +1563,18 @@ export default class SpecialVisualizations { { name: "text", required: true, - doc: "The value to show", - }, + doc: "The value to show" + } ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new FixedUiElement("{" + args[0] + "}") - }, + } }, { funcName: "tags", @@ -1577,15 +1584,15 @@ export default class SpecialVisualizations { { name: "key", defaultValue: "value", - doc: "The key to look for the tags", - }, + doc: "The key to look for the tags" + } ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const key = argument[0] ?? "value" return new VariableUiElement( @@ -1605,12 +1612,12 @@ export default class SpecialVisualizations { "Could not parse this tag: " + JSON.stringify(value) + " due to " + - e, + e ).SetClass("alert") } - }), + }) ) - }, + } }, { funcName: "giggity", @@ -1618,8 +1625,8 @@ export default class SpecialVisualizations { { name: "giggityUrl", required: true, - doc: "The URL of the giggity-XML", - }, + doc: "The URL of the giggity-XML" + } ], docs: "Shows events that are happening based on a Giggity URL", needsUrls: (args) => args[0], @@ -1629,11 +1636,11 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const giggityUrl = argument[0] return new SvelteUIElement(Giggity, { tags: tagSource, state, giggityUrl }) - }, + } }, { funcName: "gps_all_tags", @@ -1645,21 +1652,21 @@ export default class SpecialVisualizations { _: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const tags = (( state )).geolocation.currentUserLocation.features.map( - (features) => features[0]?.properties, + (features) => features[0]?.properties ) return new Combine([ new SvelteUIElement(OrientationDebugPanel, {}), new SvelteUIElement(AllTagsPanel, { state, - tags, - }), + tags + }) ]) - }, + } }, { funcName: "favourite_status", @@ -1672,15 +1679,15 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new SvelteUIElement(MarkAsFavourite, { tags: tagSource, state, layer, - feature, + feature }) - }, + } }, { funcName: "favourite_icon", @@ -1692,15 +1699,15 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new SvelteUIElement(MarkAsFavouriteMini, { tags: tagSource, state, layer, - feature, + feature }).SetClass("w-full h-full") - }, + } }, { funcName: "direction_indicator", @@ -1712,10 +1719,10 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, argument: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new SvelteUIElement(DirectionIndicator, { state, feature }) - }, + } }, { funcName: "qr_code", @@ -1725,10 +1732,10 @@ export default class SpecialVisualizations { state: SpecialVisualizationState, tags: UIEventSource>, argument: string[], - feature: Feature, + feature: Feature ): SvelteUIElement { return new SvelteUIElement(QrCode, { state, tags, feature }) - }, + } }, { funcName: "direction_absolute", @@ -1737,14 +1744,14 @@ export default class SpecialVisualizations { { name: "key", doc: "The attribute containing the degrees", - defaultValue: "_direction:centerpoint", - }, + defaultValue: "_direction:centerpoint" + } ], constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[], + args: string[] ): BaseUIElement { const key = args[0] === "" ? "_direction:centerpoint" : args[0] return new VariableUiElement( @@ -1755,13 +1762,13 @@ export default class SpecialVisualizations { }) .mapD((value) => { const dir = GeoOperations.bearingToHuman( - GeoOperations.parseBearing(value), + GeoOperations.parseBearing(value) ) console.log("Human dir", dir) return Translations.t.general.visualFeedback.directionsAbsolute[dir] - }), + }) ) - }, + } }, { funcName: "compare_data", @@ -1770,18 +1777,18 @@ export default class SpecialVisualizations { { name: "url", required: true, - doc: "The attribute containing the url where to fetch more data", + doc: "The attribute containing the url where to fetch more data" }, { name: "host", required: true, - doc: "The domain name(s) where data might be fetched from - this is needed to set the CSP. A domain must include 'https', e.g. 'https://example.com'. For multiple domains, separate them with ';'. If you don't know the possible domains, use '*'. ", + doc: "The domain name(s) where data might be fetched from - this is needed to set the CSP. A domain must include 'https', e.g. 'https://example.com'. For multiple domains, separate them with ';'. If you don't know the possible domains, use '*'. " }, { name: "readonly", required: false, - doc: "If 'yes', will not show 'apply'-buttons", - }, + doc: "If 'yes', will not show 'apply'-buttons" + } ], docs: "Gives an interactive element which shows a tag comparison between the OSM-object and the upstream object. This allows to copy some or all tags into OSM", constr( @@ -1789,7 +1796,7 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { const url = args[0] const readonly = args[3] === "yes" @@ -1801,9 +1808,9 @@ export default class SpecialVisualizations { layer, feature, readonly, - externalData, + externalData }) - }, + } }, { funcName: "login_button", @@ -1815,14 +1822,14 @@ export default class SpecialVisualizations { tagSource: UIEventSource>, args: string[], feature: Feature, - layer: LayerConfig, + layer: LayerConfig ): BaseUIElement { return new Toggle( undefined, new SvelteUIElement(LoginButton, { osmConnection: state.osmConnection }), - state.osmConnection.isLoggedIn, + state.osmConnection.isLoggedIn ) - }, + } }, { funcName: "linked_data_from_website", @@ -1831,26 +1838,26 @@ export default class SpecialVisualizations { { name: "key", defaultValue: "website", - doc: "Attempt to load ld+json from the specified URL. This can be in an embedded