From b84655f2cb4959d012437b54e6e265f1d33cf97a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 25 Jul 2025 23:26:28 +0200 Subject: [PATCH 01/12] Themes(food): make minzoom uniform for food and ice_cream in theme --- assets/svg/license_info.json | 8 -------- assets/themes/food/food.json | 10 +++++++--- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/assets/svg/license_info.json b/assets/svg/license_info.json index 90ca8bed06..9ecb69183f 100644 --- a/assets/svg/license_info.json +++ b/assets/svg/license_info.json @@ -69,14 +69,6 @@ ], "sources": [] }, - { - "path": "blocked.svg", - "license": "CC0-1.0", - "authors": [ - "Pieter Vander Vennet" - ], - "sources": [] - }, { "path": "brick_wall_raw.svg", "license": "CC0-1.0", diff --git a/assets/themes/food/food.json b/assets/themes/food/food.json index efa88a30c0..be2178915b 100644 --- a/assets/themes/food/food.json +++ b/assets/themes/food/food.json @@ -40,10 +40,14 @@ }, "icon": "./assets/layers/food/restaurant.svg", "layers": [ - "food", - "ice_cream", + { + "builtin": ["food","ice_cream"], + "override": { + "minzoom": 11 + } + }, "outdoor_seating", "food_courts" ], "widenFactor": 3 -} \ No newline at end of file +} From 56a55e8b54b4c1274d142a2e9bc85cbcec437a3d Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 26 Jul 2025 00:30:39 +0200 Subject: [PATCH 02/12] Refactoring: remove some old, UIElement based code --- src/Logic/State/UserSettingsMetaTagging.ts | 48 ++---- src/UI/Base/VariableUIElement.ts | 1 - src/UI/Input/README.md | 1 - src/UI/Input/TextField.ts | 144 ------------------ src/UI/InputElement/Helpers/TimeInput.svelte | 10 ++ .../{ => Helpers}/WikidataInputHelper.svelte | 12 +- src/UI/InputElement/InputHelper.svelte | 6 +- src/UI/InputElement/InputHelpers.ts | 2 +- src/UI/InputElement/Validator.ts | 11 +- src/UI/InputElement/Validators.ts | 3 + .../InputElement/Validators/TimeValidator.ts | 11 ++ .../OpeningHours/PublicHolidaySelector.svelte | 22 +-- .../Visualisation/OpeningHoursHeader.svelte | 3 - src/UI/i18n/Translation.ts | 45 +----- 14 files changed, 58 insertions(+), 261 deletions(-) delete mode 100644 src/UI/Input/README.md delete mode 100644 src/UI/Input/TextField.ts create mode 100644 src/UI/InputElement/Helpers/TimeInput.svelte rename src/UI/InputElement/{ => Helpers}/WikidataInputHelper.svelte (82%) create mode 100644 src/UI/InputElement/Validators/TimeValidator.ts diff --git a/src/Logic/State/UserSettingsMetaTagging.ts b/src/Logic/State/UserSettingsMetaTagging.ts index 6e568c5c32..33a5ae85b5 100644 --- a/src/Logic/State/UserSettingsMetaTagging.ts +++ b/src/Logic/State/UserSettingsMetaTagging.ts @@ -1,42 +1,14 @@ import { Utils } from "../../Utils" /** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */ export class ThemeMetaTagging { - public static readonly themeName = "usersettings" + public static readonly themeName = "usersettings" - public metaTaggging_for_usersettings(feat: { properties: Record }) { - Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () => - feat.properties._description - .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/) - ?.at(1) - ) - Utils.AddLazyProperty( - feat.properties, - "_d", - () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? "" - ) - Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () => - ((feat) => { - const e = document.createElement("div") - e.innerHTML = feat.properties._d - return Array.from(e.getElementsByTagName("a")).filter( - (a) => a.href.match(/mastodon|en.osm.town/) !== null - )[0]?.href - })(feat) - ) - Utils.AddLazyProperty(feat.properties, "_mastodon_link", () => - ((feat) => { - const e = document.createElement("div") - e.innerHTML = feat.properties._d - return Array.from(e.getElementsByTagName("a")).filter( - (a) => a.getAttribute("rel")?.indexOf("me") >= 0 - )[0]?.href - })(feat) - ) - Utils.AddLazyProperty( - feat.properties, - "_mastodon_candidate", - () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a - ) - feat.properties["__current_backgroun"] = "initial_value" - } -} + public metaTaggging_for_usersettings(feat: {properties: Record}) { + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) ) + Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' ) + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) ) + Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) ) + Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a ) + feat.properties['__current_backgroun'] = 'initial_value' + } +} \ No newline at end of file diff --git a/src/UI/Base/VariableUIElement.ts b/src/UI/Base/VariableUIElement.ts index c288d86399..7b91270539 100644 --- a/src/UI/Base/VariableUIElement.ts +++ b/src/UI/Base/VariableUIElement.ts @@ -1,6 +1,5 @@ import { Store } from "../../Logic/UIEventSource" import BaseUIElement from "../BaseUIElement" -import Combine from "./Combine" import { Utils } from "../../Utils" /** diff --git a/src/UI/Input/README.md b/src/UI/Input/README.md deleted file mode 100644 index 82b6ff5477..0000000000 --- a/src/UI/Input/README.md +++ /dev/null @@ -1 +0,0 @@ -This is the old, deprecated directory. New, Svelte-based items go into `InputElement` diff --git a/src/UI/Input/TextField.ts b/src/UI/Input/TextField.ts deleted file mode 100644 index 573ad4e290..0000000000 --- a/src/UI/Input/TextField.ts +++ /dev/null @@ -1,144 +0,0 @@ -import { Store, UIEventSource } from "../../Logic/UIEventSource" -import BaseUIElement from "../BaseUIElement" -import { Translation } from "../i18n/Translation" -import Locale from "../i18n/Locale" - -/** - * @deprecated - */ -interface TextFieldOptions { - placeholder?: string | Store | Translation - value?: UIEventSource - htmlType?: "text" | "time" | string -} - -/** - * @deprecated - */ -export class TextField extends BaseUIElement { - public readonly enterPressed = new UIEventSource(undefined) - private readonly value: UIEventSource - private _actualField: HTMLElement - private readonly _rawValue: UIEventSource - private _isFocused = false - private readonly _options: TextFieldOptions - - constructor(options?: TextFieldOptions) { - super() - this._options = options ?? {} - options = options ?? {} - this.value = options?.value ?? new UIEventSource(undefined) - this._rawValue = new UIEventSource("") - } - - private static SetCursorPosition(textfield: HTMLElement, i: number) { - if (textfield === undefined || textfield === null) { - return - } - if (i === -1) { - // @ts-ignore - i = textfield.value.length - } - textfield.focus() - // @ts-ignore - textfield.setSelectionRange(i, i) - } - - protected InnerConstructElement(): HTMLElement { - const options = this._options - const self = this - let placeholderStore: Store - let placeholder: string = "" - if (options.placeholder) { - if (typeof options.placeholder === "string") { - placeholder = options.placeholder - placeholderStore = undefined - } else { - if ( - options.placeholder instanceof Store && - options.placeholder["data"] !== undefined - ) { - placeholderStore = options.placeholder - } else if ( - options.placeholder instanceof Translation && - options.placeholder["translations"] !== undefined - ) { - placeholderStore = >( - Locale.language.map((l) => (options.placeholder).textFor(l)) - ) - } - placeholder = placeholderStore?.data ?? placeholder ?? "" - } - } - - this.SetClass("form-text-field") - let inputEl: HTMLElement - { - const el = document.createElement("input") - el.type = options.htmlType ?? "text" - el.placeholder = placeholder - el.style.cssText = "width: 100%;" - el.dir = "auto" - inputEl = el - if (placeholderStore) { - placeholderStore.addCallbackAndRunD((placeholder) => (el.placeholder = placeholder)) - } - } - - const form = document.createElement("form") - form.appendChild(inputEl) - form.onsubmit = () => false - - const field = inputEl - - this.value.addCallbackAndRunD((value) => { - // We leave the textfield as is in the case of undefined or null (handled by addCallbackAndRunD) - make sure we do not erase it! - field["value"] = value - }) - - field.oninput = () => { - // How much characters are on the right, not including spaces? - // @ts-ignore - const endDistance = field.value.substring(field.selectionEnd).replace(/ /g, "").length - // @ts-ignore - let val: string = field.value - self._rawValue.setData(val) - if (!val) { - self.value.setData(undefined) - } else { - self.value.setData(val) - } - // Setting the value might cause the value to be set again. We keep the distance _to the end_ stable, as phone number formatting might cause the start to change - // See https://source.mapcomplete.org/MapComplete/MapComplete/issues/103 - // We reread the field value - it might have changed! - - // @ts-ignore - val = field.value - let newCursorPos = val.length - endDistance - while ( - newCursorPos >= 0 && - // We count the number of _actual_ characters (non-space characters) on the right of the new value - // This count should become bigger then the end distance - val.substr(newCursorPos).replace(/ /g, "").length < endDistance - ) { - newCursorPos-- - } - TextField.SetCursorPosition(field, newCursorPos) - } - - field.addEventListener("keyup", function (event) { - if (event.key === "Enter") { - // @ts-ignore - self.enterPressed.setData(field.value) - } - }) - - if (this._isFocused) { - field.focus() - } - - this._actualField = field - return form - } - -} diff --git a/src/UI/InputElement/Helpers/TimeInput.svelte b/src/UI/InputElement/Helpers/TimeInput.svelte new file mode 100644 index 0000000000..6a76196edb --- /dev/null +++ b/src/UI/InputElement/Helpers/TimeInput.svelte @@ -0,0 +1,10 @@ + + + diff --git a/src/UI/InputElement/WikidataInputHelper.svelte b/src/UI/InputElement/Helpers/WikidataInputHelper.svelte similarity index 82% rename from src/UI/InputElement/WikidataInputHelper.svelte rename to src/UI/InputElement/Helpers/WikidataInputHelper.svelte index 213ec89679..2992207085 100644 --- a/src/UI/InputElement/WikidataInputHelper.svelte +++ b/src/UI/InputElement/Helpers/WikidataInputHelper.svelte @@ -4,14 +4,14 @@ Wrapper around 'WikidataInput.svelte' which handles the arguments */ - import { UIEventSource } from "../../Logic/UIEventSource" - import Locale from "../i18n/Locale" - import { Utils } from "../../Utils" - import Wikidata from "../../Logic/Web/Wikidata" - import WikidataInput from "./Helpers/WikidataInput.svelte" + import { UIEventSource } from "../../../Logic/UIEventSource" + import Locale from "../../i18n/Locale" + import { Utils } from "../../../Utils" + import Wikidata from "../../../Logic/Web/Wikidata" + import WikidataInput from "../Helpers/WikidataInput.svelte" import type { Feature } from "geojson" import { onDestroy } from "svelte" - import WikidataValidator from "./Validators/WikidataValidator" + import WikidataValidator from "../Validators/WikidataValidator" export let args: (string | number | boolean)[] = [] export let feature: Feature diff --git a/src/UI/InputElement/InputHelper.svelte b/src/UI/InputElement/InputHelper.svelte index 7b4dbcbe40..c0dddc7da8 100644 --- a/src/UI/InputElement/InputHelper.svelte +++ b/src/UI/InputElement/InputHelper.svelte @@ -18,8 +18,9 @@ import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte" import SlopeInput from "./Helpers/SlopeInput.svelte" import type { SpecialVisualizationState } from "../SpecialVisualization" - import WikidataInputHelper from "./WikidataInputHelper.svelte" + import WikidataInputHelper from "./Helpers/WikidataInputHelper.svelte" import DistanceInput from "./Helpers/DistanceInput.svelte" + import TimeInput from "./Helpers/TimeInput.svelte" export let type: ValidatorType export let value: UIEventSource @@ -39,6 +40,8 @@ /> {:else if type === "date"} +{:else if type === "time"} + {:else if type === "color"} {:else if type === "image"} @@ -55,6 +58,7 @@ {:else if type === "distance"} + {:else} {/if} diff --git a/src/UI/InputElement/InputHelpers.ts b/src/UI/InputElement/InputHelpers.ts index 1f83161345..a7d25514b5 100644 --- a/src/UI/InputElement/InputHelpers.ts +++ b/src/UI/InputElement/InputHelpers.ts @@ -25,7 +25,7 @@ export interface InputHelperProperties { } export default class InputHelpers { - public static hideInputField: string[] = ["translation", "simple_tag", "tag"] + public static hideInputField: string[] = ["translation", "simple_tag", "tag","time"] /** * Constructs a mapProperties-object for the given properties. diff --git a/src/UI/InputElement/Validator.ts b/src/UI/InputElement/Validator.ts index f37dbbc1b7..b6cc2f4d42 100644 --- a/src/UI/InputElement/Validator.ts +++ b/src/UI/InputElement/Validator.ts @@ -1,5 +1,6 @@ import { Translation } from "../i18n/Translation" import Translations from "../i18n/Translations" +import { HTMLInputTypeAttribute } from "svelte/elements" /** * A 'TextFieldValidator' contains various methods to check and cleanup an entered value or to give feedback. @@ -16,15 +17,7 @@ export abstract class Validator { * What HTML-inputmode to use? * Note: some inputHelpers will completely hide the default text field. This is kept in InputHelpers.hideInputField */ - public readonly inputmode?: - | "none" - | "text" - | "tel" - | "url" - | "email" - | "numeric" - | "decimal" - | "search" + public readonly inputmode?: HTMLInputTypeAttribute public readonly textArea: boolean public readonly isMeta?: boolean diff --git a/src/UI/InputElement/Validators.ts b/src/UI/InputElement/Validators.ts index 9b236858c5..ce0d5945b8 100644 --- a/src/UI/InputElement/Validators.ts +++ b/src/UI/InputElement/Validators.ts @@ -28,6 +28,7 @@ import VeloparkValidator from "./Validators/VeloparkValidator" import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValidator" import CurrencyValidator from "./Validators/CurrencyValidator" import RegexValidator from "./Validators/RegexValidator" +import { TimeValidator } from "./Validators/TimeValidator" export type ValidatorType = (typeof Validators.availableTypes)[number] @@ -36,6 +37,7 @@ export default class Validators { "color", "currency", "date", + "time", "direction", "distance", "email", @@ -68,6 +70,7 @@ export default class Validators { new StringValidator(), new TextValidator(), new DateValidator(), + new TimeValidator(), new NatValidator(), new IntValidator(), new DistanceValidator(), diff --git a/src/UI/InputElement/Validators/TimeValidator.ts b/src/UI/InputElement/Validators/TimeValidator.ts new file mode 100644 index 0000000000..867d352265 --- /dev/null +++ b/src/UI/InputElement/Validators/TimeValidator.ts @@ -0,0 +1,11 @@ +import { Validator } from "../Validator" + +export class TimeValidator extends Validator { + + inputmode = "time" + + constructor() { + super("time", "A time picker") + } + +} diff --git a/src/UI/OpeningHours/PublicHolidaySelector.svelte b/src/UI/OpeningHours/PublicHolidaySelector.svelte index c50b3ec753..4fdb8809b5 100644 --- a/src/UI/OpeningHours/PublicHolidaySelector.svelte +++ b/src/UI/OpeningHours/PublicHolidaySelector.svelte @@ -3,9 +3,8 @@ import Dropdown from "../Base/Dropdown.svelte" import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" - import ToSvelte from "../Base/ToSvelte.svelte" - import { TextField } from "../Input/TextField" import { OH } from "./OpeningHours" + import TimeInput from "../InputElement/Helpers/TimeInput.svelte" export let value: UIEventSource let startValue: UIEventSource = new UIEventSource(undefined) @@ -71,22 +70,11 @@ {#if $mode === " "} -
+
- + - + +
{/if} diff --git a/src/UI/OpeningHours/Visualisation/OpeningHoursHeader.svelte b/src/UI/OpeningHours/Visualisation/OpeningHoursHeader.svelte index aedbdb01be..c0ffb34965 100644 --- a/src/UI/OpeningHours/Visualisation/OpeningHoursHeader.svelte +++ b/src/UI/OpeningHours/Visualisation/OpeningHoursHeader.svelte @@ -1,7 +1,4 @@ + +{#if allTheSame && range[0]?.length > 0} +
+ + {#each range[0] as moment (moment)} +
{moment.startDate.toLocaleTimeString()}
+ {/each} +
+{:else if dayZero >= 0 } + {#each range as moments, i (moments)} +
+ + {#if range[i].length > 0} + {#each moments as moment (moment)} +
{moment.startDate.toLocaleTimeString()}
+ {/each} + {:else} + + {/if} +
+ {/each} +{/if} diff --git a/src/UI/CollectionTimes/CollectionTimes.svelte b/src/UI/CollectionTimes/CollectionTimes.svelte new file mode 100644 index 0000000000..3bdd96afe3 --- /dev/null +++ b/src/UI/CollectionTimes/CollectionTimes.svelte @@ -0,0 +1,55 @@ + + + +
+ + {#if everyDaySame || !weekdaysAndWeekendsSame} + + + + {:else if times.isWeekStable()} +
+ + + + + + + + +
+ {:else} + {#each ranges as range (range)} + {#if range.length > 0} +
+ {range[0].startDate.toLocaleDateString()} +
+ {#each range as moment} +
+ {moment.startDate.toLocaleTimeString()} +
+ {/each} +
+
+ {/if} + {/each} + {/if} +
diff --git a/src/UI/InputElement/Helpers/CollectionTimes/CollectionTimes.svelte b/src/UI/InputElement/Helpers/CollectionTimes/CollectionTimes.svelte new file mode 100644 index 0000000000..850c3589da --- /dev/null +++ b/src/UI/InputElement/Helpers/CollectionTimes/CollectionTimes.svelte @@ -0,0 +1,48 @@ + + +
+ + {#each $singleRules as rule} + + + {#if $singleRules.length > 1} + + + {/if} + + + {/each} + +
diff --git a/src/UI/InputElement/Helpers/CollectionTimes/SingleCollectionTime.svelte b/src/UI/InputElement/Helpers/CollectionTimes/SingleCollectionTime.svelte new file mode 100644 index 0000000000..f0328841fa --- /dev/null +++ b/src/UI/InputElement/Helpers/CollectionTimes/SingleCollectionTime.svelte @@ -0,0 +1,122 @@ + + +
+ +
+ +
+ {#each $values as value, i} +
+ + {#if $values.length > 1} + + {/if} +
+ {/each} + +
+
+ {#each daysOfTheWeek as day, i} +
+ + + +
+ {/each} +
+ +
+
+ +
+ + + +
+ +
+
diff --git a/src/UI/InputElement/InputHelper.svelte b/src/UI/InputElement/InputHelper.svelte index c0dddc7da8..200e2afc26 100644 --- a/src/UI/InputElement/InputHelper.svelte +++ b/src/UI/InputElement/InputHelper.svelte @@ -21,6 +21,7 @@ import WikidataInputHelper from "./Helpers/WikidataInputHelper.svelte" import DistanceInput from "./Helpers/DistanceInput.svelte" import TimeInput from "./Helpers/TimeInput.svelte" + import CollectionTimes from "./Helpers/CollectionTimes/CollectionTimes.svelte" export let type: ValidatorType export let value: UIEventSource @@ -42,6 +43,8 @@ {:else if type === "time"} +{:else if type === "points_in_time"} + {:else if type === "color"} {:else if type === "image"} diff --git a/src/UI/InputElement/InputHelpers.ts b/src/UI/InputElement/InputHelpers.ts index a7d25514b5..bae5aaec9e 100644 --- a/src/UI/InputElement/InputHelpers.ts +++ b/src/UI/InputElement/InputHelpers.ts @@ -3,6 +3,7 @@ import { UIEventSource } from "../../Logic/UIEventSource" import { MapProperties } from "../../Models/MapProperties" import { Feature } from "geojson" import { GeoOperations } from "../../Logic/GeoOperations" +import { ValidatorType } from "./Validators" export interface InputHelperProperties { /** @@ -25,11 +26,12 @@ export interface InputHelperProperties { } export default class InputHelpers { - public static hideInputField: string[] = ["translation", "simple_tag", "tag","time"] + public static hideInputField: ValidatorType[] = ["translation", "simple_tag", "tag","time"] /** * Constructs a mapProperties-object for the given properties. * Assumes that the first helper-args contains the desired zoom-level + * Used for the 'direction' input helper * @param properties * @private */ diff --git a/src/UI/InputElement/Validators.ts b/src/UI/InputElement/Validators.ts index ce0d5945b8..6c830c2135 100644 --- a/src/UI/InputElement/Validators.ts +++ b/src/UI/InputElement/Validators.ts @@ -29,6 +29,7 @@ import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValida import CurrencyValidator from "./Validators/CurrencyValidator" import RegexValidator from "./Validators/RegexValidator" import { TimeValidator } from "./Validators/TimeValidator" +import CollectionTimesValidator from "./Validators/CollectionTimesValidator" export type ValidatorType = (typeof Validators.availableTypes)[number] @@ -54,6 +55,7 @@ export default class Validators { "pfloat", "phone", "pnat", + "points_in_time", "regex", "simple_tag", "slope", @@ -97,6 +99,7 @@ export default class Validators { new NameSuggestionIndexValidator(), new CurrencyValidator(), new RegexValidator(), + new CollectionTimesValidator() ] private static _byType = Validators._byTypeConstructor() diff --git a/src/UI/InputElement/Validators/CollectionTimesValidator.ts b/src/UI/InputElement/Validators/CollectionTimesValidator.ts new file mode 100644 index 0000000000..5847657c83 --- /dev/null +++ b/src/UI/InputElement/Validators/CollectionTimesValidator.ts @@ -0,0 +1,7 @@ +import StringValidator from "./StringValidator" + +export default class CollectionTimesValidator extends StringValidator{ + constructor() { + super("points_in_time", "'Points in time' are points according to a fixed schedule, e.g. 'every monday at 10:00'. They are typically used for postbox collection times or times of mass at a place of worship") + } +} diff --git a/src/UI/OpeningHours/OpeningHours.ts b/src/UI/OpeningHours/OpeningHours.ts index c4facd9abb..ab4b53d627 100644 --- a/src/UI/OpeningHours/OpeningHours.ts +++ b/src/UI/OpeningHours/OpeningHours.ts @@ -1,5 +1,5 @@ import { Utils } from "../../Utils" -import opening_hours from "opening_hours" +import opening_hours, { mode, optional_conf } from "opening_hours" import { Store } from "../../Logic/UIEventSource" import { Translation, TypedTranslation } from "../i18n/Translation" import Translations from "../i18n/Translations" @@ -25,7 +25,7 @@ export interface OpeningRange { * Various utilities manipulating opening hours */ export class OH { - private static readonly days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] + public static readonly days = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"] private static readonly daysIndexed = { mo: 0, tu: 1, @@ -570,7 +570,8 @@ changes // => [[36000,61200], ["10:00", "17:00"]] public static createOhObject( tags: Record & { _lat: number; _lon: number; _country?: string }, textToParse: string, - country: string + country: string, + mode?: mode, ) { return new opening_hours( textToParse, @@ -582,11 +583,61 @@ changes // => [[36000,61200], ["10:00", "17:00"]] state: undefined, }, }, - { tag_key: "opening_hours" } + { + tag_key: "opening_hours", + mode, + }, + ) } + /** + * Constructs the opening-ranges for either this week, or for next week if there are no more openings this week. + * Note: 'today' is mostly used for testing + * + * const oh = new opening_hours("mar 15 - oct 15") + * const ranges = OH.createRangesForApplicableWeek(oh, new Date(2025,4,20,10,0,0)) + * ranges // => {ranges: [[],[],[],[],[],[],[]], startingMonday: new Date(2025,4,18,24,0,0)} + */ + public static createRangesForApplicableWeek( + oh: opening_hours, + today?: Date, + ): { + ranges: OpeningRange[][] + startingMonday: Date + } { + today ??= new Date() + today.setHours(0, 0, 0, 0) + const lastMonday = OH.getMondayBefore(today) + const nextSunday = new Date(lastMonday) + nextSunday.setDate(nextSunday.getDate() + 7) + + if (!oh.getState() && !oh.getUnknown()) { + // POI is currently closed + const nextChange: Date = oh.getNextChange() + if ( + // Shop isn't gonna open anymore in this timerange + nextSunday < nextChange && + // And we are already in the weekend to show next week + (today.getDay() == 0 || today.getDay() == 6) + ) { + // We move the range to next week! + lastMonday.setDate(lastMonday.getDate() + 7) + nextSunday.setDate(nextSunday.getDate() + 7) + } + } + + /* We calculate the ranges when it is opened! */ + return { startingMonday: lastMonday, ranges: OH.getRanges(oh, lastMonday, nextSunday) } + } + /** + * + * Checks that the days are identical + * + * @param startday start index, inclusive + * @param endday end index, INCLUSIVE (!) + * * let ranges = [ * [ * { @@ -666,47 +717,6 @@ changes // => [[36000,61200], ["10:00", "17:00"]] * OH.weekdaysIdentical(ranges, 4, 5) // => false * */ - - /** - * Constructs the opening-ranges for either this week, or for next week if there are no more openings this week. - * Note: 'today' is mostly used for testing - * - * const oh = new opening_hours("mar 15 - oct 15") - * const ranges = OH.createRangesForApplicableWeek(oh, new Date(2025,4,20,10,0,0)) - * ranges // => {ranges: [[],[],[],[],[],[],[]], startingMonday: new Date(2025,4,18,24,0,0)} - */ - public static createRangesForApplicableWeek( - oh: opening_hours, - today?: Date - ): { - ranges: OpeningRange[][] - startingMonday: Date - } { - today ??= new Date() - today.setHours(0, 0, 0, 0) - const lastMonday = OH.getMondayBefore(today) - const nextSunday = new Date(lastMonday) - nextSunday.setDate(nextSunday.getDate() + 7) - - if (!oh.getState() && !oh.getUnknown()) { - // POI is currently closed - const nextChange: Date = oh.getNextChange() - if ( - // Shop isn't gonna open anymore in this timerange - nextSunday < nextChange && - // And we are already in the weekend to show next week - (today.getDay() == 0 || today.getDay() == 6) - ) { - // We move the range to next week! - lastMonday.setDate(lastMonday.getDate() + 7) - nextSunday.setDate(nextSunday.getDate() + 7) - } - } - - /* We calculate the ranges when it is opened! */ - return { startingMonday: lastMonday, ranges: OH.getRanges(oh, lastMonday, nextSunday) } - } - public static weekdaysIdentical(openingRanges: OpeningRange[][], startday = 0, endday = 4) { const monday = openingRanges[startday] for (let i = startday + 1; i <= endday; i++) { diff --git a/src/UI/OpeningHours/Visualisation/OpeningHours.svelte b/src/UI/OpeningHours/Visualisation/OpeningHours.svelte index 25e1eff5d2..bdf41324fa 100644 --- a/src/UI/OpeningHours/Visualisation/OpeningHours.svelte +++ b/src/UI/OpeningHours/Visualisation/OpeningHours.svelte @@ -4,7 +4,7 @@ */ import type { OpeningRange } from "../OpeningHours" - import { OH, ToTextualDescription } from "../OpeningHours" + import { ToTextualDescription } from "../OpeningHours" import opening_hours from "opening_hours" import { ariaLabel } from "../../../Utils/ariaLabel" import RegularOpeningHoursTable from "./RegularOpeningHoursTable.svelte" diff --git a/src/UI/Popup/DataVisualisations.ts b/src/UI/Popup/DataVisualisations.ts index fc9f6bc7d7..a643209bf1 100644 --- a/src/UI/Popup/DataVisualisations.ts +++ b/src/UI/Popup/DataVisualisations.ts @@ -1,8 +1,4 @@ -import { - SpecialVisualization, - SpecialVisualizationState, - SpecialVisualizationSvelte, -} from "../SpecialVisualization" +import { SpecialVisualization, SpecialVisualizationState, SpecialVisualizationSvelte } from "../SpecialVisualization" import { HistogramViz } from "./HistogramViz" import { Store, UIEventSource } from "../../Logic/UIEventSource" import { Feature } from "geojson" @@ -28,6 +24,7 @@ import TagRenderingEditable from "./TagRendering/TagRenderingEditable.svelte" import AllTagsPanel from "./AllTagsPanel/AllTagsPanel.svelte" import { FixedUiElement } from "../Base/FixedUiElement" import { TagUtils } from "../../Logic/Tags/TagUtils" +import CollectionTimes from "../CollectionTimes/CollectionTimes.svelte" class DirectionIndicatorVis extends SpecialVisualization { funcName = "direction_indicator" @@ -41,7 +38,7 @@ class DirectionIndicatorVis extends SpecialVisualization { state: SpecialVisualizationState, tagSource: UIEventSource>, argument: string[], - feature: Feature + feature: Feature, ): BaseUIElement { return new SvelteUIElement(DirectionIndicator, { state, feature }) } @@ -69,7 +66,7 @@ class DirectionAbsolute extends SpecialVisualization { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - args: string[] + args: string[], ): BaseUIElement { const key = args[0] === "" ? "_direction:centerpoint" : args[0] const offset = args[1] === "" ? 0 : Number(args[1]) @@ -82,11 +79,11 @@ class DirectionAbsolute extends SpecialVisualization { }) .mapD((value) => { const dir = GeoOperations.bearingToHuman( - GeoOperations.parseBearing(value) + offset + GeoOperations.parseBearing(value) + offset, ) console.log("Human dir", dir) return Translations.t.general.visualFeedback.directionsAbsolute[dir] - }) + }), ) } } @@ -156,7 +153,7 @@ class OpeningHoursState extends SpecialVisualizationSvelte { constr( state: SpecialVisualizationState, tags: UIEventSource>, - args: string[] + args: string[], ): SvelteUIElement { const keyToUse = args[0] const prefix = args[1] @@ -198,7 +195,7 @@ class Canonical extends SpecialVisualization { return undefined } const allUnits: Unit[] = [].concat( - ...(state?.theme?.layers?.map((lyr) => lyr.units) ?? []) + ...(state?.theme?.layers?.map((lyr) => lyr.units) ?? []), ) const unit = allUnits.filter((unit) => unit.isApplicableToKey(key))[0] if (unit === undefined) { @@ -206,7 +203,7 @@ class Canonical extends SpecialVisualization { } const getCountry = () => tagSource.data._country return unit.asHumanLongValue(value, getCountry) - }) + }), ) } } @@ -231,7 +228,7 @@ class PresetDescription extends SpecialVisualization { constr( state: SpecialVisualizationState, - tagSource: UIEventSource> + tagSource: UIEventSource>, ): BaseUIElement { const translation = tagSource.map((tags) => { const layer = state.theme.getMatchingLayer(tags) @@ -251,7 +248,7 @@ class PresetTypeSelect extends SpecialVisualizationSvelte { tags: UIEventSource>, argument: string[], selectedElement: Feature, - layer: LayerConfig + layer: LayerConfig, ): SvelteUIElement { const t = Translations.t.preset_type if (layer._basedOn !== layer.id) { @@ -312,7 +309,7 @@ class TagsVis extends SpecialVisualization { constr( state: SpecialVisualizationState, tagSource: UIEventSource>, - argument: string[] + argument: string[], ): BaseUIElement { const key = argument[0] ?? "value" return new VariableUiElement( @@ -329,14 +326,37 @@ class TagsVis extends SpecialVisualization { return parsed.asHumanString(true, false, {}) } catch (e) { return new FixedUiElement( - "Could not parse this tag: " + JSON.stringify(value) + " due to " + e + "Could not parse this tag: " + JSON.stringify(value) + " due to " + e, ).SetClass("alert") } - }) + }), ) } } +class PointsInTimeVis extends SpecialVisualization { + docs = "Creates a visualisation for 'points in time', e.g. collection times of a postbox" + group = "data" + funcName = "points_in_time" + args = [ + { + name: "key", + required: true, + doc: "The key out of which the points_in_time will be parsed", + }, + ] + + constr(state: SpecialVisualizationState, tagSource: UIEventSource>, args: string[], feature: Feature, layer: LayerConfig): BaseUIElement { + const key = args[0] + const points_in_time = tagSource.map(tags => tags[key]) + const times = points_in_time.map(times => + OH.createOhObject(tagSource.data, times, tagSource.data["_country"], 1), [tagSource]) + return new VariableUiElement(times.map(times => + new SvelteUIElement(CollectionTimes, { times }), + )) + } +} + export class DataVisualisations { public static initList(): SpecialVisualization[] { return [ @@ -346,6 +366,7 @@ export class DataVisualisations { new DirectionIndicatorVis(), new OpeningHoursTableVis(), new OpeningHoursState(), + new PointsInTimeVis(), new Canonical(), new LanguageElement(), new PresetDescription(), From afb94ecefba4a07ade404e833e17746e0c7bacd4 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 28 Jul 2025 01:00:25 +0200 Subject: [PATCH 08/12] Chore: small fixes and updates --- src/Logic/UIEventSource.ts | 2 +- src/UI/BaseUIElement.ts | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index c9f5f2f3d0..4d8812b8da 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -121,7 +121,7 @@ export class Stores { } src.addCallbackD((contents) => { for (let i = 0; i < contents.length; i++) { - sources[i].setData(contents[i]) + sources[i]?.setData(contents[i]) } }) return src diff --git a/src/UI/BaseUIElement.ts b/src/UI/BaseUIElement.ts index f7e87f38fe..aef860aa13 100644 --- a/src/UI/BaseUIElement.ts +++ b/src/UI/BaseUIElement.ts @@ -73,9 +73,6 @@ export default abstract class BaseUIElement { return this } - /** - * The same as 'Render', but creates a HTML element instead of the HTML representation - */ public ConstructElement(): HTMLElement { if (typeof window === undefined) { return undefined From d7b83e0d880c42c8d23ea8b3347e45698a28fb4c Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 28 Jul 2025 01:17:12 +0200 Subject: [PATCH 09/12] Refactoring: cleanup of no longer necessary code --- src/Logic/DetermineTheme.ts | 4 -- src/UI/Base/Combine.ts | 4 -- src/UI/Base/SvelteUIElement.ts | 5 --- src/UI/BaseUIElement.ts | 26 ----------- src/UI/Popup/DataVisualisations.ts | 44 ------------------- .../Popup/ImportButtons/WayImportButtonViz.ts | 4 +- src/UI/i18n/Translations.ts | 28 +----------- src/index.ts | 2 + 8 files changed, 4 insertions(+), 113 deletions(-) diff --git a/src/Logic/DetermineTheme.ts b/src/Logic/DetermineTheme.ts index 584317db14..26a69145a5 100644 --- a/src/Logic/DetermineTheme.ts +++ b/src/Logic/DetermineTheme.ts @@ -208,10 +208,6 @@ export default class DetermineTheme { private static async LoadRemoteTheme(link: string): Promise { console.log("Downloading map theme from ", link) - new FixedUiElement(`Downloading the theme from the link...`).AttachTo( - "maindiv" - ) - let parsed = await Utils.downloadJson(link) let forcedId = parsed.id const url = new URL(link) diff --git a/src/UI/Base/Combine.ts b/src/UI/Base/Combine.ts index 44c904944d..5d9f807127 100644 --- a/src/UI/Base/Combine.ts +++ b/src/UI/Base/Combine.ts @@ -25,10 +25,6 @@ export default class Combine extends BaseUIElement { } } - public getElements(): BaseUIElement[] { - return this.uiElements - } - protected InnerConstructElement(): HTMLElement { const el = document.createElement("span") try { diff --git a/src/UI/Base/SvelteUIElement.ts b/src/UI/Base/SvelteUIElement.ts index aba7386c6c..fcb8d0e2c0 100644 --- a/src/UI/Base/SvelteUIElement.ts +++ b/src/UI/Base/SvelteUIElement.ts @@ -45,11 +45,6 @@ export default class SvelteUIElement< this._slots = slots } - public setSpan() { - this.tag = "span" - return this - } - protected InnerConstructElement(): HTMLElement { const el = document.createElement(this.tag) new this._svelteComponent({ diff --git a/src/UI/BaseUIElement.ts b/src/UI/BaseUIElement.ts index aef860aa13..c5539a6303 100644 --- a/src/UI/BaseUIElement.ts +++ b/src/UI/BaseUIElement.ts @@ -13,33 +13,7 @@ export default abstract class BaseUIElement { protected readonly clss: Set = new Set() protected style: string private _onClick: () => void | Promise - AttachTo(divId: string) { - const element = document.getElementById(divId) - if (element === null) { - if (Utils.runningFromConsole) { - this.ConstructElement() - return - } - throw "SEVERE: could not attach UIElement to " + divId - } - let alreadyThere = false - const elementToAdd = this.ConstructElement() - const childs = Array.from(element.childNodes) - for (const child of childs) { - if (child === elementToAdd) { - alreadyThere = true - continue - } - element.removeChild(child) - } - - if (elementToAdd !== undefined && !alreadyThere) { - element.appendChild(elementToAdd) - } - - return this - } /** * Adds all the relevant classes, space separated */ diff --git a/src/UI/Popup/DataVisualisations.ts b/src/UI/Popup/DataVisualisations.ts index a643209bf1..0f7987055c 100644 --- a/src/UI/Popup/DataVisualisations.ts +++ b/src/UI/Popup/DataVisualisations.ts @@ -22,8 +22,6 @@ import { And } from "../../Logic/Tags/And" import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" import TagRenderingEditable from "./TagRendering/TagRenderingEditable.svelte" import AllTagsPanel from "./AllTagsPanel/AllTagsPanel.svelte" -import { FixedUiElement } from "../Base/FixedUiElement" -import { TagUtils } from "../../Logic/Tags/TagUtils" import CollectionTimes from "../CollectionTimes/CollectionTimes.svelte" class DirectionIndicatorVis extends SpecialVisualization { @@ -293,47 +291,6 @@ class AllTagsVis extends SpecialVisualizationSvelte { } } -class TagsVis extends SpecialVisualization { - funcName = "tags" - docs = "Shows a (json of) tags in a human-readable way + links to the wiki" - - args = [ - { - name: "key", - type: "key", - defaultValue: "value", - doc: "The key to look for the tags", - }, - ] - - constr( - state: SpecialVisualizationState, - tagSource: UIEventSource>, - argument: string[], - ): BaseUIElement { - const key = argument[0] ?? "value" - return new VariableUiElement( - tagSource.map((tags) => { - let value = tags[key] - if (!value) { - return new FixedUiElement("No tags found").SetClass("font-bold") - } - if (typeof value === "string" && value.startsWith("{")) { - value = JSON.parse(value) - } - try { - const parsed = TagUtils.Tag(value) - return parsed.asHumanString(true, false, {}) - } catch (e) { - return new FixedUiElement( - "Could not parse this tag: " + JSON.stringify(value) + " due to " + e, - ).SetClass("alert") - } - }), - ) - } -} - class PointsInTimeVis extends SpecialVisualization { docs = "Creates a visualisation for 'points in time', e.g. collection times of a postbox" group = "data" @@ -372,7 +329,6 @@ export class DataVisualisations { new PresetDescription(), new PresetTypeSelect(), new AllTagsVis(), - new TagsVis(), ] } } diff --git a/src/UI/Popup/ImportButtons/WayImportButtonViz.ts b/src/UI/Popup/ImportButtons/WayImportButtonViz.ts index 4a061fef6f..61272532f8 100644 --- a/src/UI/Popup/ImportButtons/WayImportButtonViz.ts +++ b/src/UI/Popup/ImportButtons/WayImportButtonViz.ts @@ -6,7 +6,6 @@ import BaseUIElement from "../../BaseUIElement" import { ImportFlowUtils } from "./ImportFlow" import LayerConfig from "../../../Models/ThemeConfig/LayerConfig" import SvelteUIElement from "../../Base/SvelteUIElement" -import { FixedUiElement } from "../../Base/FixedUiElement" import WayImportFlow from "./WayImportFlow.svelte" import WayImportFlowState, { WayImportFlowArguments } from "./WayImportFlowState" import { Utils } from "../../../Utils" @@ -70,8 +69,7 @@ export default class WayImportButtonViz extends SpecialVisualization implements ): BaseUIElement { const geometry = feature.geometry if (!(geometry.type == "LineString" || geometry.type === "Polygon")) { - console.error("Invalid type to import", geometry.type) - return new FixedUiElement("Invalid geometry type:" + geometry.type).SetClass("alert") + throw "Invalid type to import " + geometry.type } const args: WayImportFlowArguments = Utils.ParseVisArgs(this.args, argument) const tagsToApply = ImportFlowUtils.getTagsToApply(tagSource, args) diff --git a/src/UI/i18n/Translations.ts b/src/UI/i18n/Translations.ts index 3d8515615a..0ab0408baf 100644 --- a/src/UI/i18n/Translations.ts +++ b/src/UI/i18n/Translations.ts @@ -1,6 +1,4 @@ -import { FixedUiElement } from "../Base/FixedUiElement" import { Translation, TypedTranslation } from "./Translation" -import BaseUIElement from "../BaseUIElement" import CompiledTranslations from "../../assets/generated/CompiledTranslations" import LanguageUtils from "../../Utils/LanguageUtils" import { Store } from "../../Logic/UIEventSource" @@ -12,31 +10,7 @@ export default class Translations { static readonly t: Readonly = CompiledTranslations.t private static knownLanguages = LanguageUtils.usedLanguages - constructor() { - throw "Translations is static. If you want to intitialize a new translation, use the singular form" - } - - /** - * @deprecated - */ - public static W(s: string | number | boolean | BaseUIElement): BaseUIElement { - if (typeof s === "string") { - return new FixedUiElement(s) - } - if (typeof s === "number") { - return new FixedUiElement("" + s).SetClass("font-bold") - } - if (typeof s === "boolean") { - return new FixedUiElement("" + s).SetClass("font-bold") - } - if (typeof s === "object") { - if (s.ConstructElement) { - return s - } - const v = JSON.stringify(s) - return new FixedUiElement(v).SetClass("literal-code") - } - return s + private constructor() { } /** diff --git a/src/index.ts b/src/index.ts index 87a53dee3b..462d8fa9c1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,6 +11,8 @@ async function main() { console.error("Huh? Couldn't remove child!") } try { + + document.getElementById("maindiv").innerHTML = "
Loading map...
" const theme = await DetermineTheme.getTheme() new SingleThemeGui({ target, From 67915e1c689c17d168d3b9a7c4d35e690d404e95 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 28 Jul 2025 01:17:38 +0200 Subject: [PATCH 10/12] Chore: auto-reordering of some themes --- assets/layers/icons/icons.json | 4 +- assets/layers/last_click/last_click.json | 4 +- assets/layers/note/note.json | 4 +- .../osm_community_index.json | 12 +- .../mapcomplete-changes.json | 314 +++++++++--------- public/css/index-tailwind-output.css | 9 + src/Logic/DetermineTheme.ts | 1 - src/UI/BaseUIElement.ts | 1 - 8 files changed, 178 insertions(+), 171 deletions(-) diff --git a/assets/layers/icons/icons.json b/assets/layers/icons/icons.json index 93e9e91ec0..54ebd45d54 100644 --- a/assets/layers/icons/icons.json +++ b/assets/layers/icons/icons.json @@ -430,10 +430,10 @@ } }, { - "condition": "_favourite=yes", + "id": "favourite_icon", "description": "Only for rendering", "icon": "circle:white;heart:red", - "id": "favourite_icon", + "condition": "_favourite=yes", "metacondition": "__showTimeSensitiveIcons!=no" }, { diff --git a/assets/layers/last_click/last_click.json b/assets/layers/last_click/last_click.json index 6aaa191159..3185c513fb 100644 --- a/assets/layers/last_click/last_click.json +++ b/assets/layers/last_click/last_click.json @@ -217,8 +217,8 @@ }, { "id": "debug", - "metacondition": "__featureSwitchIsDebugging=true", - "render": "{all_tags()}" + "render": "{all_tags()}", + "metacondition": "__featureSwitchIsDebugging=true" } ], "filter": [ diff --git a/assets/layers/note/note.json b/assets/layers/note/note.json index eedef08c68..b1dd36f327 100644 --- a/assets/layers/note/note.json +++ b/assets/layers/note/note.json @@ -114,9 +114,9 @@ "lineRendering": [], "tagRenderings": [ { - "classes": "p-0", "id": "conversation", - "render": "{visualize_note_comments()}" + "render": "{visualize_note_comments()}", + "classes": "p-0" }, { "id": "add_image", diff --git a/assets/layers/osm_community_index/osm_community_index.json b/assets/layers/osm_community_index/osm_community_index.json index 150fc6ecd9..1c605ca388 100644 --- a/assets/layers/osm_community_index/osm_community_index.json +++ b/assets/layers/osm_community_index/osm_community_index.json @@ -66,16 +66,16 @@ ], "tagRenderings": [ { - "condition": "level=country", - "description": "The name of the country", "id": "country_name", - "render": "{nameEn} {emojiFlag}" + "description": "The name of the country", + "render": "{nameEn} {emojiFlag}", + "condition": "level=country" }, { - "condition": "_community_links~*", - "description": "Community Links (Discord, meetups, Slack groups, IRC channels, mailing lists etc...)", "id": "community_links", - "render": "{_community_links}" + "description": "Community Links (Discord, meetups, Slack groups, IRC channels, mailing lists etc...)", + "render": "{_community_links}", + "condition": "_community_links~*" } ], "filter": [ diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index d05bec44e7..80382fd51d 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -2,40 +2,40 @@ "id": "mapcomplete-changes", "title": { "en": "Changes made with MapComplete", - "de": "Änderungen mit MapComplete", "cs": "Změny provedené pomocí MapComplete", + "da": "Ændringer foretaget med MapComplete", + "de": "Änderungen mit MapComplete", "es": "Cambios realizados con MapComplete", "fr": "Modifications faites avec MapComplete", - "nl": "Wijzigingen gemaakt met MapComplete", - "ko": "MapComplete로 이루어진 변경 사항", "it": "Modifiche fatte con MapComplete", - "zh_Hant": "由MapComplete進行的變動", - "da": "Ændringer foretaget med MapComplete" + "ko": "MapComplete로 이루어진 변경 사항", + "nl": "Wijzigingen gemaakt met MapComplete", + "zh_Hant": "由MapComplete進行的變動" }, "description": { "en": "This maps shows all the changes made with MapComplete", + "cs": "Tyto mapy zobrazují všechny změny provedené pomocí MapComplete", + "da": "Dette kort viser alle de ændringer, der er foretaget med MapComplete", "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", "es": "Este mapa muestra todos los cambios realizados con MapComplete", - "pl": "Ta mapa pokazuje wszystkie zmiany wprowadzone za pomocą MapComplete", - "cs": "Tyto mapy zobrazují všechny změny provedené pomocí MapComplete", "fr": "Cette carte montre tous les changements effectués avec MapComplete", - "nl": "Deze kaarten tonen alle wijzigingen die zijn gemaakt met MapComplete", - "ko": "이 지도는 MapComplete를 사용하여 이루어진 모든 변경 사항을 표시합니다", "it": "Questa mappa mostra tutte le modifiche effettuate con MapComplete", - "zh_Hant": "這個地圖顯示所有用MapComplete做出的改變", - "da": "Dette kort viser alle de ændringer, der er foretaget med MapComplete" + "ko": "이 지도는 MapComplete를 사용하여 이루어진 모든 변경 사항을 표시합니다", + "nl": "Deze kaarten tonen alle wijzigingen die zijn gemaakt met MapComplete", + "pl": "Ta mapa pokazuje wszystkie zmiany wprowadzone za pomocą MapComplete", + "zh_Hant": "這個地圖顯示所有用MapComplete做出的改變" }, "shortDescription": { "en": "Shows changes made by MapComplete", - "de": "Zeigt die von MapComplete vorgenommenen Änderungen an", "cs": "Zobrazuje změny provedené nástrojem MapComplete", + "da": "Viser ændringer foretaget af MapComplete", + "de": "Zeigt die von MapComplete vorgenommenen Änderungen an", "es": "Muestra los cambios realizados por MapComplete", "fr": "Afficher les modifications faites avec MapComplete", - "nl": "Toont wijzigingen gemaakt met MapComplete", - "ko": "MapComplete를 통해 이루어진 변경 사항을 표시합니다", "it": "Mostra le modifiche fatte con MapComplete", - "zh_Hant": "顯示由MapComplete進行的變動", - "da": "Viser ændringer foretaget af MapComplete" + "ko": "MapComplete를 통해 이루어진 변경 사항을 표시합니다", + "nl": "Toont wijzigingen gemaakt met MapComplete", + "zh_Hant": "顯示由MapComplete進行的變動" }, "icon": "./assets/svg/logo.svg", "startZoom": 1, @@ -47,15 +47,15 @@ "id": "mapcomplete-changes", "name": { "en": "Changeset centers", + "cs": "Changeset centra", + "da": "Ændringssæt centre", "de": "Changeset-Zentren", "es": "Centros de conjuntos de cambios", "fr": "Centre du groupe de modifications", - "nl": "Changeset centra", - "cs": "Changeset centra", - "ko": "주요 변경 사항", "it": "Centri di changeset", - "zh_Hant": "變更集的中心位置", - "da": "Ændringssæt centre" + "ko": "주요 변경 사항", + "nl": "Changeset centra", + "zh_Hant": "變更集的中心位置" }, "minzoom": 0, "source": { @@ -66,97 +66,97 @@ "title": { "render": { "en": "Changeset for {theme}", - "de": "Änderungssatz für {theme}", "cs": "Sada změn pro {theme}", + "da": "Ændringssæt for {theme}", + "de": "Änderungssatz für {theme}", "es": "Conjunto de cambios para {theme}", - "nl": "Changeset voor {theme}", - "ko": "{theme}에 대한 변경 사항", "it": "Changeset per {theme}", - "zh_Hant": "{theme} 的變更集", - "da": "Ændringssæt for {theme}" + "ko": "{theme}에 대한 변경 사항", + "nl": "Changeset voor {theme}", + "zh_Hant": "{theme} 的變更集" } }, "description": { "en": "Shows all MapComplete changes", + "cs": "Zobrazí všechny změny MapComplete", + "da": "Viser alle MapComplete-ændringer", "de": "Zeigt alle MapComplete-Änderungen", "es": "Muestra todos los cambios de MapComplete", - "cs": "Zobrazí všechny změny MapComplete", - "nl": "Toon alle MapComplete-wijzigingen", - "ko": "MapComplete의 모든 변화 보기", "it": "Mostra tutte le modifiche di MapComplete", - "zh_Hant": "顯示所有用MapComplete做出的變動", - "da": "Viser alle MapComplete-ændringer" + "ko": "MapComplete의 모든 변화 보기", + "nl": "Toon alle MapComplete-wijzigingen", + "zh_Hant": "顯示所有用MapComplete做出的變動" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { "en": "Changeset {id}", - "de": "Änderungssatz {id}", "cs": "Sada změn {id}", + "da": "Ændringssæt {id}", + "de": "Änderungssatz {id}", "es": "Conjunto de cambios {id}", - "nl": "Changeset {id}", - "ko": "변경사항{id}", "it": "Changeset {id}", - "zh_Hant": "變更集{id}", - "da": "Ændringssæt {id}" + "ko": "변경사항{id}", + "nl": "Changeset {id}", + "zh_Hant": "變更集{id}" } }, { "id": "contributor", "question": { "en": "What contributor did make this change?", - "de": "Wer hat zu dieser Änderung beigetragen?", "cs": "Který přispěvatel provedl tuto změnu?", + "da": "Hvilken bidragsyder lavede denne ændring?", + "de": "Wer hat zu dieser Änderung beigetragen?", "es": "¿Qué colaborador realizó este cambio?", - "nl": "Welke bijdrager maakte deze verandering?", - "ko": "이 변화를 만든 기여자는 누구입니까?", "it": "Quale contributore ha fatto questa modifica?", - "zh_Hant": "貢獻者在這一變動做了什麼?", - "da": "Hvilken bidragsyder lavede denne ændring?" + "ko": "이 변화를 만든 기여자는 누구입니까?", + "nl": "Welke bijdrager maakte deze verandering?", + "zh_Hant": "貢獻者在這一變動做了什麼?" }, "freeform": { "key": "user" }, "render": { "en": "Change made by {user}", - "de": "Änderung vorgenommen von {user}", "cs": "Změna provedena uživatelem {user}", + "da": "Ændringer lavet af {user}", + "de": "Änderung vorgenommen von {user}", "es": "Cambio realizado por {user}", "fr": "Modification faite par {user}", - "nl": "Wijziging aangebracht door {user}", - "ko": "변경 사항은 {user}에 의해 만들어졌습니다", "it": "Modifica effettuata da {user}", - "zh_Hant": "由{user}做的變動", - "da": "Ændringer lavet af {user}" + "ko": "변경 사항은 {user}에 의해 만들어졌습니다", + "nl": "Wijziging aangebracht door {user}", + "zh_Hant": "由{user}做的變動" } }, { "id": "theme-id", "question": { "en": "What theme was used to make this change?", - "de": "Welches Thema wurde für diese Änderung verwendet?", "cs": "Jaký motiv byl použit k provedení této změny?", + "da": "Hvilket tema blev brugt til at foretage denne ændring?", + "de": "Welches Thema wurde für diese Änderung verwendet?", "es": "¿Qué tema se utilizó para realizar este cambio?", - "nl": "Welk thema werd gebruikt voor deze wijziging?", - "ko": "이 변경을 수행하는 데 무슨 테마가 사용되었나요?", "it": "Quale tema è stato utilizzato per effettuare questa modifica?", - "zh_Hant": "那個主題進行變動的?", - "da": "Hvilket tema blev brugt til at foretage denne ændring?" + "ko": "이 변경을 수행하는 데 무슨 테마가 사용되었나요?", + "nl": "Welk thema werd gebruikt voor deze wijziging?", + "zh_Hant": "那個主題進行變動的?" }, "freeform": { "key": "theme" }, "render": { "en": "Change with theme {theme}", + "cs": "Změna pomocí tématu {theme}", + "da": "Ændret med teamet {theme}", "de": "Änderung mit Thema {theme}", "es": "Cambio con el tema {theme}", - "nl": "Verander met thema {theme}", - "cs": "Změna pomocí tématu {theme}", - "ko": "테마 변경 사항{theme}", "it": "Modifica con tema {theme}", - "zh_Hant": "由主題改變{theme}", - "da": "Ændret med teamet {theme}" + "ko": "테마 변경 사항{theme}", + "nl": "Verander met thema {theme}", + "zh_Hant": "由主題改變{theme}" } }, { @@ -166,50 +166,50 @@ }, "question": { "en": "What locale (language) was this change made in?", - "de": "In welcher Sprache (Locale) wurde diese Änderung vorgenommen?", "cs": "V jakém prostředí (jazyce) byla tato změna provedena?", + "da": "Hvilket lokalsprog blev denne ændring foretaget i?", + "de": "In welcher Sprache (Locale) wurde diese Änderung vorgenommen?", "es": "¿En qué configuración regional (idioma) se realizó este cambio?", - "nl": "In welke 'locale' (taal) is deze wijziging gemaakt?", - "ko": "이 변경은 어떤 지역(언어)로 이루어졌나요?", "it": "In quale locale (lingua) è stata effettuata questa modifica?", - "zh_Hant": "這個變動是用什麼當地 (語言)?", - "da": "Hvilket lokalsprog blev denne ændring foretaget i?" + "ko": "이 변경은 어떤 지역(언어)로 이루어졌나요?", + "nl": "In welke 'locale' (taal) is deze wijziging gemaakt?", + "zh_Hant": "這個變動是用什麼當地 (語言)?" }, "render": { "en": "User locale is {locale}", - "de": "Die Benutzersprache ist {locale}", "cs": "Uživatelské prostředí je {locale}", + "da": "Brugers lokalsprog er {locale}", + "de": "Die Benutzersprache ist {locale}", "es": "Configuración regional del usuario es {locale}", - "nl": "De gebruikerstaal (locale) is {locale}", - "ko": "사용자 지역은 {locale} 입니다", "it": "Il locale dell'utente è {locale}", - "zh_Hant": "使用者所在地是 {locale}", - "da": "Brugers lokalsprog er {locale}" + "ko": "사용자 지역은 {locale} 입니다", + "nl": "De gebruikerstaal (locale) is {locale}", + "zh_Hant": "使用者所在地是 {locale}" } }, { "id": "host", "render": { "en": "Change with with {host}", - "de": "Änderung mit {host}", "cs": "Změnit pomocí {host}", + "da": "Ændring lavet med {host}", + "de": "Änderung mit {host}", "es": "Cambio realizado con {host}", - "nl": "Gewijzigd met {host}", - "ko": "{host}를 사용하여 변경되었습니다", "it": "Modifica effettuata con {host}", - "zh_Hant": "{host}做出的變動", - "da": "Ændring lavet med {host}" + "ko": "{host}를 사용하여 변경되었습니다", + "nl": "Gewijzigd met {host}", + "zh_Hant": "{host}做出的變動" }, "question": { "en": "What host (website) was this change made with?", - "de": "Bei welchem Host (Website) wurde diese Änderung vorgenommen?", "cs": "U jakého hostitele (webové stránky) byla tato změna provedena?", + "da": "Hvilken host (websted) blev denne ændring lavet med?", + "de": "Bei welchem Host (Website) wurde diese Änderung vorgenommen?", "es": "¿Con qué anfitrión (sitio web) se realizó este cambio?", - "nl": "Met welke host (website) is deze wijziging gemaakt?", - "ko": "이 변경은 어떤 호스트(웹사이트)를 사용하여 이루어졌나요?", "it": "Con quale host (sito web) è stata effettuata questa modifica?", - "zh_Hant": "什麼主辦方 (網站) 做出這一變動?", - "da": "Hvilken host (websted) blev denne ændring lavet med?" + "ko": "이 변경은 어떤 호스트(웹사이트)를 사용하여 이루어졌나요?", + "nl": "Met welke host (website) is deze wijziging gemaakt?", + "zh_Hant": "什麼主辦方 (網站) 做出這一變動?" }, "freeform": { "key": "host" @@ -232,9 +232,9 @@ "question": { "en": "With what platform was the change made?", "cs": "S jakou platformou byly změny provedeny?", + "da": "Med hvilken platform blev ændringen foretaget?", "it": "Con quale piattaforma è stata effettuata la modifica?", - "zh_Hant": "那個平台進行變動?", - "da": "Med hvilken platform blev ændringen foretaget?" + "zh_Hant": "那個平台進行變動?" }, "mappings": [ { @@ -242,9 +242,9 @@ "then": { "en": "Made on the web", "cs": "Vytvořeno na webu", + "da": "Lavet på nettet", "it": "Fatto sul web", - "zh_Hant": "由網站進行", - "da": "Lavet på nettet" + "zh_Hant": "由網站進行" } }, { @@ -252,9 +252,9 @@ "then": { "en": "Made with the android app", "cs": "Vytvořeno s aplikací pro Android", + "da": "Lavet med android-appen", "it": "Fatto con l'app Android", - "zh_Hant": "由Android app進行", - "da": "Lavet med android-appen" + "zh_Hant": "由Android app進行" } } ] @@ -263,27 +263,27 @@ "id": "version", "question": { "en": "What version of MapComplete was used to make this change?", - "de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?", "cs": "Jaká verze aplikace MapComplete byla použita k provedení této změny?", + "da": "Hvilken version af MapComplete blev brugt til at foretage denne ændring?", + "de": "Welche Version von MapComplete wurde verwendet, um diese Änderung vorzunehmen?", "es": "¿Qué versión de MapComplete se utilizó para realizar este cambio?", "fr": "Quelle version de MapCompletee a été utilisée pour faire cette modification ?", - "nl": "Welke versie van MapComplete is gebruikt voor deze wijziging?", - "ko": "이 변경을 수행하는 데 사용된 MapComplete의 버전은 무엇인가요?", "it": "Quale versione di MapComplete è stata utilizzata per effettuare questa modifica?", - "zh_Hant": "那個版本的MapComplete用來進行改變?", - "da": "Hvilken version af MapComplete blev brugt til at foretage denne ændring?" + "ko": "이 변경을 수행하는 데 사용된 MapComplete의 버전은 무엇인가요?", + "nl": "Welke versie van MapComplete is gebruikt voor deze wijziging?", + "zh_Hant": "那個版本的MapComplete用來進行改變?" }, "render": { "en": "Made with {editor}", - "de": "Erstellt mit {editor}", "cs": "Vytvořeno pomocí {editor}", + "da": "Lavet med {editor}", + "de": "Erstellt mit {editor}", "es": "Hecho con {editor}", "fr": "Fait avec {editor}", - "nl": "Gemaakt met {editor}", - "ko": "{editor}에 의해 만들어졌습니다", "it": "Fatto con {editor}", - "zh_Hant": "由 {editor} 進行", - "da": "Lavet med {editor}" + "ko": "{editor}에 의해 만들어졌습니다", + "nl": "Gemaakt met {editor}", + "zh_Hant": "由 {editor} 進行" }, "freeform": { "key": "editor" @@ -718,16 +718,16 @@ ], "question": { "en": "Themename contains {search}", + "cs": "Název obsahuje {search}", + "da": "Temanavnet indeholder {search}", "de": "Themenname enthält {search}", "es": "El nombre del tema contiene {search}", - "pl": "Nazwa tematu zawiera {search}", - "cs": "Název obsahuje {search}", "fr": "Le nom du thème contient {search}", - "nl": "Themanaam bevat {search}", - "ko": "테마 이름에 {search}가 포함되어 있습니다", "it": "Il nome del tema contiene {search}", - "zh_Hant": "含有主題名稱 {search}", - "da": "Temanavnet indeholder {search}" + "ko": "테마 이름에 {search}가 포함되어 있습니다", + "nl": "Themanaam bevat {search}", + "pl": "Nazwa tematu zawiera {search}", + "zh_Hant": "含有主題名稱 {search}" } } ] @@ -744,15 +744,15 @@ ], "question": { "en": "Themename does not contain {search}", + "cs": "Název motivu neobsahuje {search}", + "da": "Temanavnet indeholder ikke {search}", "de": "Themename enthält nicht {search}", "es": "El nombre del tema no contiene {search}", - "cs": "Název motivu neobsahuje {search}", "fr": "Le nom du thème ne contient pas {search}", - "nl": "Themanaam bevat geen {search}", - "ko": "테마 이름에 {search}가 포함되어 있지 않습니다", "it": "Il nome del tema non contiene {search}", - "zh_Hant": "主題名稱並不包含 {search}", - "da": "Temanavnet indeholder ikke {search}" + "ko": "테마 이름에 {search}가 포함되어 있지 않습니다", + "nl": "Themanaam bevat geen {search}", + "zh_Hant": "主題名稱並不包含 {search}" } } ] @@ -769,15 +769,15 @@ ], "question": { "en": "Made by contributor {search}", + "cs": "Vytvořeno přispěvatelem {search}", + "da": "Lavet af bidragsyder {search}", "de": "Erstellt von Mitwirkendem {search}", "es": "Hecho por el colaborador {search}", - "cs": "Vytvořeno přispěvatelem {search}", "fr": "Fait par le·a contributeur·trice {search}", - "nl": "Toegevoegd door {search}", - "ko": "기여자 {search}에 의해 만들어졌습니다", "it": "Fatto dal contributore {search}", - "zh_Hant": "由貢獻者進行 {search}", - "da": "Lavet af bidragsyder {search}" + "ko": "기여자 {search}에 의해 만들어졌습니다", + "nl": "Toegevoegd door {search}", + "zh_Hant": "由貢獻者進行 {search}" } } ] @@ -794,15 +794,15 @@ ], "question": { "en": "Not made by contributor {search}", + "cs": "Nevytvořeno přispěvatelem {search}", + "da": "Ikke lavet af bidragsyder {search}", "de": "Nicht erstellt von Mitwirkendem {search}", "es": "No hecho por el colaborador {search}", - "cs": "Nevytvořeno přispěvatelem {search}", "fr": "Pas fait par le·a contributeur·trice {search}", - "nl": "Niet toegevoegd door {search}", - "ko": "{search} 기여자가 만든 것이 아닙니다", "it": "Non fatto dal contributore {search}", - "zh_Hant": "並非由貢獻者進行 {search}", - "da": "Ikke lavet af bidragsyder {search}" + "ko": "{search} 기여자가 만든 것이 아닙니다", + "nl": "Niet toegevoegd door {search}", + "zh_Hant": "並非由貢獻者進行 {search}" } } ] @@ -820,15 +820,15 @@ ], "question": { "en": "Made before {search}", + "cs": "Vytvořeno před {search}", + "da": "Lavet før {search}", "de": "Erstellt vor {search}", "es": "Hecho antes de {search}", - "cs": "Vytvořeno před {search}", "fr": "Fait avant {search}", - "nl": "Toegevoegd vóór {search}", - "ko": "{search} 이전에 만들어졌습니다", "it": "Fatto prima di {search}", - "zh_Hant": "之前進行的 {search}", - "da": "Lavet før {search}" + "ko": "{search} 이전에 만들어졌습니다", + "nl": "Toegevoegd vóór {search}", + "zh_Hant": "之前進行的 {search}" } } ] @@ -846,15 +846,15 @@ ], "question": { "en": "Made after {search}", + "cs": "Vytvořeno po {search}", + "da": "Lavet efter {search}", "de": "Erstellt nach {search}", "es": "Hecho después de {search}", - "cs": "Vytvořeno po {search}", "fr": "Fait après {search}", - "nl": "Toegevoegd na {search}", - "ko": "{search} 이후에 만들어졌습니다", "it": "Fatto dopo {search}", - "zh_Hant": "之後進行的 {search}", - "da": "Lavet efter {search}" + "ko": "{search} 이후에 만들어졌습니다", + "nl": "Toegevoegd na {search}", + "zh_Hant": "之後進行的 {search}" } } ] @@ -871,15 +871,15 @@ ], "question": { "en": "User language (iso-code) {search}", + "cs": "Jazyk uživatele (iso-kód) {search}", + "da": "Brugersprog (iso-code) {search}", "de": "Benutzersprache (ISO-Code) {search}", "es": "Idioma del usuario (código ISO) {search}", - "cs": "Jazyk uživatele (iso-kód) {search}", "fr": "Langage utilisateur (code iso) {search}", - "nl": "Gebruikerstaal (iso-code) {search}", - "ko": "사용자 언어 (iso-code) {search}", "it": "Lingua utente (codice iso) {search}", - "zh_Hant": "使用者語言 (iso-代碼) {search}", - "da": "Brugersprog (iso-code) {search}" + "ko": "사용자 언어 (iso-code) {search}", + "nl": "Gebruikerstaal (iso-code) {search}", + "zh_Hant": "使用者語言 (iso-代碼) {search}" } } ] @@ -896,14 +896,14 @@ ], "question": { "en": "Made with host {search}", - "de": "Erstellt mit Host {search}", "cs": "Vytvořeno pomocí hostitele {search}", + "da": "Lavet med host {search}", + "de": "Erstellt mit Host {search}", "es": "Hecho con el anfitrión {search}", - "nl": "Gemaakt met {search}", - "ko": "호스트 {search}에 의해 만들어졌습니다", "it": "Fatto con host {search}", - "zh_Hant": "由主辦方進行 {search}", - "da": "Lavet med host {search}" + "ko": "호스트 {search}에 의해 만들어졌습니다", + "nl": "Gemaakt met {search}", + "zh_Hant": "由主辦方進行 {search}" } } ] @@ -915,13 +915,13 @@ "question": { "en": "Changeset added at least one image", "cs": "Sada změn přidala alespoň jeden obrázek", + "da": "Ændringssæt tilføjet mindst ét billede", "de": "Changeset hat mindestens ein Bild hinzugefügt", "es": "El conjunto de cambios agregó al menos una imagen", + "it": "Il changeset ha aggiunto almeno un'immagine", "ko": "주요 변경사항에 최소 하나의 이미지가 추가 되었습니다", "nl": "Changeset voegde minstens één afbeelding toe", - "it": "Il changeset ha aggiunto almeno un'immagine", - "zh_Hant": "變更集必須加至少一張照片", - "da": "Ændringssæt tilføjet mindst ét billede" + "zh_Hant": "變更集必須加至少一張照片" }, "osmTags": "add-image>0" } @@ -934,14 +934,14 @@ "osmTags": "theme!=grb", "question": { "en": "Exclude GRB theme", - "de": "GRB-Thema ausschließen", "cs": "Vyloučit motiv GRB", + "da": "Ekskludér GRB-tema", + "de": "GRB-Thema ausschließen", "es": "Excluir el tema GRB", - "nl": "GRB-thema uitsluiten", - "ko": "GRB 테마 제외", "it": "Escludi il tema GRB", - "zh_Hant": "排除GRB主題", - "da": "Ekskludér GRB-tema" + "ko": "GRB 테마 제외", + "nl": "GRB-thema uitsluiten", + "zh_Hant": "排除GRB主題" } } ] @@ -953,14 +953,14 @@ "osmTags": "theme!=etymology", "question": { "en": "Exclude etymology theme", - "de": "Etymologie-Thema ausschließen", "cs": "Vyloučit etymologii tématu", + "da": "Ekskluder etymologi-temaet", + "de": "Etymologie-Thema ausschließen", "es": "Excluir el tema de etimología", - "nl": "Thema etymologie uitsluiten", - "ko": "어원 테마 제외", "it": "Escludi il tema etimologia", - "zh_Hant": "排除詞源主題", - "da": "Ekskluder etymologi-temaet" + "ko": "어원 테마 제외", + "nl": "Thema etymologie uitsluiten", + "zh_Hant": "排除詞源主題" } } ] @@ -972,10 +972,10 @@ "question": { "en": "All platforms", "cs": "Všechny platformy", + "da": "Alle platforme", "de": "Alle Plattformen", "it": "Tutte le piattaforme", - "zh_Hant": "所有平台", - "da": "Alle platforme" + "zh_Hant": "所有平台" }, "quesiton": "All platforms" }, @@ -983,10 +983,10 @@ "question": { "en": "Made with Android", "cs": "Vytvořeno s Androidem", + "da": "Lavet med Android", "de": "Mit Android erstellt", "it": "Fatto con Android", - "zh_Hant": "由Android進行", - "da": "Lavet med Android" + "zh_Hant": "由Android進行" }, "osmTags": "android=yes" }, @@ -994,10 +994,10 @@ "question": { "en": "Made on the web", "cs": "Vytvořeno na webu", + "da": "Lavet på nettet", "de": "Im Internet erstellt", "it": "Fatto sul web", - "zh_Hant": "由網路上進行", - "da": "Lavet på nettet" + "zh_Hant": "由網路上進行" }, "osmTags": "android=" } @@ -1014,15 +1014,15 @@ "id": "link_to_more", "render": { "en": "More statistics can be found here", - "de": "Weitere Statistiken findest du hier", "cs": "Další statistiky najdete zde", + "da": "Du kan finde flere statistikker her", + "de": "Weitere Statistiken findest du hier", "es": "Puedes encontrar más estadísticas aquí", "fr": "Plus de statistiques peuvent être trouvées ici", - "nl": "Meer statistieken vind je hier", - "ko": "추가 통계는 에서 확인할 수 있습니다", "it": "Altre statistiche possono essere trovate qui", - "zh_Hant": "可以在這邊找到更多統計資訊", - "da": "Du kan finde flere statistikker her" + "ko": "추가 통계는 에서 확인할 수 있습니다", + "nl": "Meer statistieken vind je hier", + "zh_Hant": "可以在這邊找到更多統計資訊" } }, { diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 3dbb3911ae..56c5498b44 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -1084,6 +1084,10 @@ input[type="range"].range-lg::-moz-range-thumb { pointer-events: auto; } +.\!visible { + visibility: visible !important; +} + .visible { visibility: visible; } @@ -1415,6 +1419,11 @@ input[type="range"].range-lg::-moz-range-thumb { margin-bottom: 0.25rem; } +.my-16 { + margin-top: 4rem; + margin-bottom: 4rem; +} + .my-2 { margin-top: 0.5rem; margin-bottom: 0.5rem; diff --git a/src/Logic/DetermineTheme.ts b/src/Logic/DetermineTheme.ts index 26a69145a5..8db8829b37 100644 --- a/src/Logic/DetermineTheme.ts +++ b/src/Logic/DetermineTheme.ts @@ -1,6 +1,5 @@ import ThemeConfig, { MinimalThemeInformation } from "../Models/ThemeConfig/ThemeConfig" import { QueryParameters } from "./Web/QueryParameters" -import { FixedUiElement } from "../UI/Base/FixedUiElement" import { Utils } from "../Utils" import { FixLegacyTheme } from "../Models/ThemeConfig/Conversion/LegacyJsonConvert" import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" diff --git a/src/UI/BaseUIElement.ts b/src/UI/BaseUIElement.ts index c5539a6303..f80165173b 100644 --- a/src/UI/BaseUIElement.ts +++ b/src/UI/BaseUIElement.ts @@ -3,7 +3,6 @@ * * Assumes a read-only configuration, so it has no 'ListenTo' */ -import { Utils } from "../Utils" /* @deprecated */ From 8b426ac444c795b38ba2add6160947aae408d098 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 28 Jul 2025 01:21:45 +0200 Subject: [PATCH 11/12] Chore: cleanup of no longer needed code --- src/UI/BaseUIElement.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/UI/BaseUIElement.ts b/src/UI/BaseUIElement.ts index f80165173b..41db9a8a12 100644 --- a/src/UI/BaseUIElement.ts +++ b/src/UI/BaseUIElement.ts @@ -11,7 +11,6 @@ export default abstract class BaseUIElement { protected isDestroyed = false protected readonly clss: Set = new Set() protected style: string - private _onClick: () => void | Promise /** * Adds all the relevant classes, space separated @@ -80,22 +79,6 @@ export default abstract class BaseUIElement { } } - if (this._onClick !== undefined) { - const self = this - el.onclick = async (e) => { - // @ts-ignore - if (e.consumed) { - return - } - const v = self._onClick() - if (typeof v === "object") { - await v - } - // @ts-ignore - e.consumed = true - } - el.classList.add("cursor-pointer") - } return el } catch (e) { From 29dc7d1e03db08264766c5a0efd674ce322e65c3 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Mon, 28 Jul 2025 01:33:11 +0200 Subject: [PATCH 12/12] Refactoring: remove dependency on 'ToSvelte' by working with TypeComponent directly --- src/Logic/ImageProviders/GenericImageProvider.ts | 2 +- src/Logic/ImageProviders/ImageProvider.ts | 7 ++----- src/Logic/ImageProviders/Imgur.ts | 3 +-- src/Logic/ImageProviders/Mapillary.ts | 6 +++--- src/Logic/ImageProviders/Panoramax.ts | 7 +++---- src/Logic/ImageProviders/WikidataImageProvider.ts | 7 +++---- src/Logic/ImageProviders/WikimediaImageProvider.ts | 9 +++------ src/UI/Base/SvelteUIElement.ts | 1 + src/UI/Image/ImageAttribution.svelte | 7 +++---- 9 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/Logic/ImageProviders/GenericImageProvider.ts b/src/Logic/ImageProviders/GenericImageProvider.ts index e69f193300..0bc0ca27fe 100644 --- a/src/Logic/ImageProviders/GenericImageProvider.ts +++ b/src/Logic/ImageProviders/GenericImageProvider.ts @@ -43,7 +43,7 @@ export default class GenericImageProvider extends ImageProvider { ] } - SourceIcon() { + sourceIcon() { return undefined } diff --git a/src/Logic/ImageProviders/ImageProvider.ts b/src/Logic/ImageProviders/ImageProvider.ts index a41a98a3b6..75c7fb1563 100644 --- a/src/Logic/ImageProviders/ImageProvider.ts +++ b/src/Logic/ImageProviders/ImageProvider.ts @@ -1,9 +1,9 @@ import { Store, Stores } from "../UIEventSource" -import BaseUIElement from "../../UI/BaseUIElement" import { LicenseInfo } from "./LicenseInfo" import { Utils } from "../../Utils" import { Feature, Point } from "geojson" import { ServerSourceInfo } from "../../Models/SourceOverview" +import { ComponentType } from "svelte/types/runtime/internal/dev" export interface ProvidedImage { url: string @@ -69,10 +69,7 @@ export default abstract class ImageProvider { public abstract readonly name: string - public abstract SourceIcon( - img?: { id: string; url: string; host?: string }, - location?: { lon: number; lat: number } - ): BaseUIElement + public abstract sourceIcon(): ComponentType /** * Gets all the relevant URLS for the given tags and for the given prefixes; diff --git a/src/Logic/ImageProviders/Imgur.ts b/src/Logic/ImageProviders/Imgur.ts index 98c5cea760..0b24091356 100644 --- a/src/Logic/ImageProviders/Imgur.ts +++ b/src/Logic/ImageProviders/Imgur.ts @@ -1,5 +1,4 @@ import ImageProvider, { ProvidedImage } from "./ImageProvider" -import BaseUIElement from "../../UI/BaseUIElement" import { Utils } from "../../Utils" import Constants from "../../Models/Constants" import { LicenseInfo } from "./LicenseInfo" @@ -27,7 +26,7 @@ export class Imgur extends ImageProvider { return [Imgur.apiUrlInfo] } - SourceIcon(): BaseUIElement { + sourceIcon() { return undefined } diff --git a/src/Logic/ImageProviders/Mapillary.ts b/src/Logic/ImageProviders/Mapillary.ts index d6c2cae7d5..f3fccd6bfe 100644 --- a/src/Logic/ImageProviders/Mapillary.ts +++ b/src/Logic/ImageProviders/Mapillary.ts @@ -2,11 +2,11 @@ import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" import { Utils } from "../../Utils" import { LicenseInfo } from "./LicenseInfo" import Constants from "../../Models/Constants" -import SvelteUIElement from "../../UI/Base/SvelteUIElement" import { default as MapillaryIcon } from "../../assets/svg/Mapillary.svelte" import { Feature, Point } from "geojson" import { Store, UIEventSource } from "../UIEventSource" import { ServerSourceInfo } from "../../Models/SourceOverview" +import { ComponentType } from "svelte/types/runtime/internal/dev" export class Mapillary extends ImageProvider { public static readonly singleton = new Mapillary() @@ -138,8 +138,8 @@ export class Mapillary extends ImageProvider { })) } - SourceIcon(): SvelteUIElement { - return new SvelteUIElement(MapillaryIcon) + sourceIcon(): ComponentType { + return MapillaryIcon } async ExtractUrls(key: string, value: string): Promise { diff --git a/src/Logic/ImageProviders/Panoramax.ts b/src/Logic/ImageProviders/Panoramax.ts index fde47448c6..0da74d158d 100644 --- a/src/Logic/ImageProviders/Panoramax.ts +++ b/src/Logic/ImageProviders/Panoramax.ts @@ -2,16 +2,15 @@ import { ImageUploader } from "./ImageUploader" import { AuthorizedPanoramax, ImageData, Panoramax, PanoramaxXYZ } from "panoramax-js/dist" import ExifReader from "exifreader" import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" -import BaseUIElement from "../../UI/BaseUIElement" import { LicenseInfo } from "./LicenseInfo" import { GeoOperations } from "../GeoOperations" import Constants from "../../Models/Constants" import { Store, Stores, UIEventSource } from "../UIEventSource" -import SvelteUIElement from "../../UI/Base/SvelteUIElement" import Panoramax_bw from "../../assets/svg/Panoramax_bw.svelte" import { Feature, Point } from "geojson" import { AddImageOptions } from "panoramax-js/dist/Panoramax" import { ServerSourceInfo } from "../../Models/SourceOverview" +import { ComponentType } from "svelte/types/runtime/internal/dev" export default class PanoramaxImageProvider extends ImageProvider { public static readonly singleton: PanoramaxImageProvider = new PanoramaxImageProvider() @@ -39,8 +38,8 @@ export default class PanoramaxImageProvider extends ImageProvider { { data: Promise<{ data: ImageData; url: string }>; time: Date } > = {} - public SourceIcon(): SvelteUIElement { - return new SvelteUIElement(Panoramax_bw) + public sourceIcon(): ComponentType { + return Panoramax_bw } visitUrl( diff --git a/src/Logic/ImageProviders/WikidataImageProvider.ts b/src/Logic/ImageProviders/WikidataImageProvider.ts index 66fdeb6905..3a774652b7 100644 --- a/src/Logic/ImageProviders/WikidataImageProvider.ts +++ b/src/Logic/ImageProviders/WikidataImageProvider.ts @@ -1,12 +1,11 @@ import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" -import BaseUIElement from "../../UI/BaseUIElement" import { WikimediaImageProvider } from "./WikimediaImageProvider" import Wikidata from "../Web/Wikidata" -import SvelteUIElement from "../../UI/Base/SvelteUIElement" import Wikidata_icon from "../../assets/svg/Wikidata.svelte" import { Utils } from "../../Utils" import { Feature, Point } from "geojson" import { ServerSourceInfo } from "../../Models/SourceOverview" +import { ComponentType } from "svelte/types/runtime/internal/dev" export class WikidataImageProvider extends ImageProvider { public static readonly singleton = new WikidataImageProvider() @@ -25,8 +24,8 @@ export class WikidataImageProvider extends ImageProvider { return Wikidata.neededUrls } - public SourceIcon(): BaseUIElement { - return new SvelteUIElement(Wikidata_icon) + public sourceIcon(): ComponentType { + return Wikidata_icon } public async ExtractUrls(key: string, value: string): Promise { diff --git a/src/Logic/ImageProviders/WikimediaImageProvider.ts b/src/Logic/ImageProviders/WikimediaImageProvider.ts index e6c6d183e3..ea5db41c78 100644 --- a/src/Logic/ImageProviders/WikimediaImageProvider.ts +++ b/src/Logic/ImageProviders/WikimediaImageProvider.ts @@ -1,13 +1,10 @@ import ImageProvider, { PanoramaView, ProvidedImage } from "./ImageProvider" -import BaseUIElement from "../../UI/BaseUIElement" -import { Utils } from "../../Utils" import { LicenseInfo } from "./LicenseInfo" import Wikimedia from "../Web/Wikimedia" -import SvelteUIElement from "../../UI/Base/SvelteUIElement" import Wikimedia_commons_white from "../../assets/svg/Wikimedia_commons_white.svelte" import { Feature, Point } from "geojson" import { ServerSourceInfo } from "../../Models/SourceOverview" -import { describe } from "vitest" +import { ComponentType } from "svelte/types/runtime/internal/dev" /** * This module provides endpoints for wikimedia and others @@ -118,8 +115,8 @@ export class WikimediaImageProvider extends ImageProvider { ) } - SourceIcon(): BaseUIElement { - return new SvelteUIElement(Wikimedia_commons_white) + sourceIcon(): ComponentType { + return Wikimedia_commons_white } public PrepUrl(value: NonNullable): ProvidedImage diff --git a/src/UI/Base/SvelteUIElement.ts b/src/UI/Base/SvelteUIElement.ts index fcb8d0e2c0..2fd1eee415 100644 --- a/src/UI/Base/SvelteUIElement.ts +++ b/src/UI/Base/SvelteUIElement.ts @@ -2,6 +2,7 @@ import BaseUIElement from "../BaseUIElement" import { SvelteComponentTyped } from "svelte" + /** * The SvelteUIComponent serves as a translating class which which wraps a SvelteElement into the BaseUIElement framework. * Also see ToSvelte.svelte for the opposite conversion diff --git a/src/UI/Image/ImageAttribution.svelte b/src/UI/Image/ImageAttribution.svelte index df3f9d1a5a..8c8f56601e 100644 --- a/src/UI/Image/ImageAttribution.svelte +++ b/src/UI/Image/ImageAttribution.svelte @@ -2,7 +2,6 @@ import { LicenseInfo } from "../../Logic/ImageProviders/LicenseInfo" import type { ProvidedImage } from "../../Logic/ImageProviders/ImageProvider" import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource" - import ToSvelte from "../Base/ToSvelte.svelte" import { EyeIcon } from "@rgossiaux/svelte-heroicons/solid" import Tr from "../Base/Tr.svelte" import Translations from "../i18n/Translations" @@ -17,7 +16,7 @@ let license: Store = image.license ? new ImmutableStore(image.license) : UIEventSource.FromPromise(image.provider?.DownloadAttribution(image)) - let icon = image.provider?.SourceIcon(image) + let icon = image.provider?.sourceIcon() let openOriginal = image.provider?.visitUrl(image) @@ -28,11 +27,11 @@ {#if icon !== undefined} {#if openOriginal} - + {:else}
- +
{/if} {/if}