Chore: Housekeeping

This commit is contained in:
Pieter Vander Vennet 2023-12-21 01:46:18 +01:00
parent ef0ba091eb
commit 319c0e2573
77 changed files with 2485 additions and 1727 deletions

View file

@ -17,7 +17,7 @@ export class BBox {
* Coordinates should be [[lon, lat],[lon, lat]]
* @param coordinates
*/
constructor(coordinates: [number,number][]) {
constructor(coordinates: [number, number][]) {
this.maxLat = -90
this.maxLon = -180
this.minLat = 90

View file

@ -10,11 +10,13 @@
</script>
{#if url}
<a href={url}
use:ariaLabel={Translations.t.general.attribution.seeOnMapillary}
target="_blank"
rel="noopener nofollower" >
<Mapillary />
<a
href={url}
use:ariaLabel={Translations.t.general.attribution.seeOnMapillary}
target="_blank"
rel="noopener nofollower"
>
<Mapillary />
</a>
{:else}
<Mapillary />

View file

@ -1,14 +1,42 @@
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<string, string>}) {
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(/&lt;/g,'<')?.replace(/&gt;/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<string, string> }) {
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(/&lt;/g, "<")?.replace(/&gt;/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"
}
}

View file

@ -27,7 +27,7 @@
oauth_token: QueryParameters.GetQueryParameter(
"oauth_token",
undefined,
"Used to complete the login",
"Used to complete the login"
),
})
const state = new UserRelatedState(osmConnection)
@ -37,7 +37,7 @@
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages)
let themeSearchText: UIEventSource<string | undefined> = new UIEventSource<string>(undefined)
document.addEventListener("keydown", function(event) {
document.addEventListener("keydown", function (event) {
if (event.ctrlKey && event.code === "KeyF") {
document.getElementById("theme-search")?.focus()
event.preventDefault()
@ -50,17 +50,15 @@
{
const prefix = "mapcomplete-hidden-theme-"
const userPreferences = state.osmConnection.preferencesHandler.preferences
visitedHiddenThemes = userPreferences.map(preferences => {
visitedHiddenThemes = userPreferences.map((preferences) => {
const knownIds = new Set<string>(
Object.keys(preferences)
.filter((key) => key.startsWith(prefix))
.map((key) => key.substring(prefix.length, key.length - "-enabled".length)),
.map((key) => key.substring(prefix.length, key.length - "-enabled".length))
)
return hiddenThemes.filter((theme) => knownIds.has(theme.id))
})
}
</script>
<div class="m-4 flex flex-col">
@ -87,14 +85,22 @@
</div>
</div>
<form class="flex justify-center" on:submit|preventDefault={_ => MoreScreen.applySearch(themeSearchText.data)}>
<form
class="flex justify-center"
on:submit|preventDefault={(_) => MoreScreen.applySearch(themeSearchText.data)}
>
<label
class="flex rounded-full border-2 border-black items-center my-2 w-full sm:w-1/2 neutral-label">
<SearchIcon aria-hidden="true" class="w-8 h-8" />
<input autofocus bind:value={$themeSearchText} class="mr-4 w-full" id="theme-search"
type="search"
use:placeholder={tr.searchForATheme}>
class="neutral-label my-2 flex w-full items-center rounded-full border-2 border-black sm:w-1/2"
>
<SearchIcon aria-hidden="true" class="h-8 w-8" />
<input
autofocus
bind:value={$themeSearchText}
class="mr-4 w-full"
id="theme-search"
type="search"
use:placeholder={tr.searchForATheme}
/>
</label>
</form>
@ -113,10 +119,12 @@
<Tr t={tr.previouslyHiddenTitle} />
</h3>
<p>
<Tr t={tr.hiddenExplanation.Subs({
hidden_discovered: $visitedHiddenThemes.length.toString(),
total_hidden: hiddenThemes.length.toString(),
})} />
<Tr
t={tr.hiddenExplanation.Subs({
hidden_discovered: $visitedHiddenThemes.length.toString(),
total_hidden: hiddenThemes.length.toString(),
})}
/>
</p>
</svelte:fragment>
</ThemesList>
@ -144,8 +152,6 @@
<Eye class="mr-2 h-6 w-6" />
<Tr t={Translations.t.privacy.title} />
</a>
</LoginToggle>
<Tr cls="link-underline" t={Translations.t.general.aboutMapComplete.intro} />

View file

@ -10,7 +10,6 @@
const dispatch = createEventDispatcher<{ close }>()
export let extraClasses = "p-4 md:p-6"
</script>
<!-- Draw the background over the total screen -->
@ -27,17 +26,14 @@
style="z-index: 21"
use:trapFocus
>
<div
class="content normal-background"
on:click|stopPropagation={() => {}}
>
<div class="content normal-background" on:click|stopPropagation={() => {}}>
<div class="h-full rounded-xl">
<slot />
</div>
<slot name="close-button">
<!-- The close button is placed _after_ the default slot in order to always paint it on top -->
<button
class="absolute right-10 top-10 h-8 w-8 cursor-pointer border-none bg-white rounded-full p-0"
class="absolute right-10 top-10 h-8 w-8 cursor-pointer rounded-full border-none bg-white p-0"
on:click={() => dispatch("close")}
>
<XCircleIcon />
@ -47,10 +43,10 @@
</div>
<style>
.content {
height: 100%;
border-radius: 0.5rem;
overflow-x: hidden;
box-shadow: 0 0 1rem #00000088;
}
.content {
height: 100%;
border-radius: 0.5rem;
overflow-x: hidden;
box-shadow: 0 0 1rem #00000088;
}
</style>

View file

@ -1,12 +1,19 @@
<script lang="ts">
export let text : string
export let href : string
export let classnames : string = undefined
export let download : string = undefined
export let ariaLabel : string = undefined
export let text: string
export let href: string
export let classnames: string = undefined
export let download: string = undefined
export let ariaLabel: string = undefined
export let newTab: boolean = false
</script>
<a {href} aria-label={ariaLabel} target={newTab ? "_blank" : undefined} {download} class={classnames}>
{@html text}</a>
<a
{href}
aria-label={ariaLabel}
target={newTab ? "_blank" : undefined}
{download}
class={classnames}
>
{@html text}
</a>

View file

@ -9,15 +9,14 @@
*/
const dispatch = createEventDispatcher()
export let cls = "m-0.5 p-0.5 sm:p-1 md:m-1"
export let arialabel: Translation = undefined
export let arialabel: Translation = undefined
</script>
<button
on:click={(e) => dispatch("click", e)}
on:keydown
use:ariaLabel={arialabel}
class={twJoin("relative pointer-events-auto h-fit w-fit rounded-full", cls)}
class={twJoin("pointer-events-auto relative h-fit w-fit rounded-full", cls)}
>
<slot />
</button>

View file

@ -12,7 +12,7 @@
<div
aria-modal="true"
autofocus
class="absolute top-0 right-0 h-screen w-full overflow-y-auto drop-shadow-2xl md:w-6/12 lg:w-5/12 xl:w-4/12 normal-background flex flex-col"
class="normal-background absolute top-0 right-0 flex h-screen w-full flex-col overflow-y-auto drop-shadow-2xl md:w-6/12 lg:w-5/12 xl:w-4/12"
role="dialog"
style="max-width: 100vw; max-height: 100vh"
tabindex="-1"

View file

@ -14,7 +14,7 @@
josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined))
const showButton = state.osmConnection.userDetails.map(
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible,
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible
)
function openJosm() {
@ -35,8 +35,7 @@
{#if $showButton}
<div class="flex">
<button class="flex items-center small soft grow" on:click={openJosm}>
<button class="small soft flex grow items-center" on:click={openJosm}>
<Josm_logo class="h-6 w-6 pr-2" />
<Tr t={t.editJosm} />
</button>
@ -49,5 +48,4 @@
<Tr cls="alert shrink-0 w-fit" t={t.josmNotOpened} />
{/if}
</div>
{/if}

View file

@ -11,13 +11,11 @@
export let cls: string = ""
// Text for the current language
let txt: Store<string | undefined> = t?.current
</script>
{#if $txt}
<span class={cls}>
<FromHtml src={$txt}/>
<FromHtml src={$txt} />
<WeblateLink context={t.context} />
</span>
{/if}

View file

@ -15,7 +15,6 @@
</script>
<div class="m-2 flex flex-col">
<h2 class="flex items-center">
<Filter class="h-6 w-6 pr-2" />
<Tr t={Translations.t.general.menu.filter} />

View file

@ -29,7 +29,7 @@
return state.sync(
(f) => f === 0,
[],
(b) => (b ? 0 : undefined),
(b) => (b ? 0 : undefined)
)
}
@ -48,7 +48,7 @@
} else {
mainElem?.classList?.remove("glowing-shadow")
}
}),
})
)
</script>

View file

@ -9,7 +9,8 @@
export let state: ThemeViewState
let geolocationstate = state.geolocation.geolocationState
let geopermission: Store<GeolocationPermissionState> = state.geolocation.geolocationState.permission
let geopermission: Store<GeolocationPermissionState> =
state.geolocation.geolocationState.permission
let allowMoving = geolocationstate.allowMoving
let currentGPSLocation = state.geolocation.geolocationState.currentGPSLocation
let geolocationControlState = state.geolocationControl
@ -18,7 +19,7 @@
{#if !$allowMoving}
<Location_locked class="h-8 w-8" />
{:else if $currentGPSLocation !== undefined }
{:else if $currentGPSLocation !== undefined}
<!-- If we have a location; this implies that the location access was granted -->
{#if $lastClickWasRecent}
<Location_unlocked class="h-8 w-8" />
@ -29,15 +30,9 @@
<Location class="h-8 w-8" />
{:else if $geopermission === "requested"}
<!-- Even though disabled, when clicking we request the location again in case the contributor dismissed the location popup -->
<Location
class="h-8 w-8"
style="animation: 3s linear 0s infinite normal none running spin;"
/>
<Location class="h-8 w-8" style="animation: 3s linear 0s infinite normal none running spin;" />
{:else if $geopermission === "denied"}
<Location_refused class="h-8 w-8" />
{:else}
<Location
class="h-8 w-8"
style="animation: 3s linear 0s infinite normal none running spin;"
/>
<Location class="h-8 w-8" style="animation: 3s linear 0s infinite normal none running spin;" />
{/if}

View file

@ -21,7 +21,7 @@
onDestroy(
triggerSearch.addCallback((_) => {
performSearch()
}),
})
)
let isRunning: boolean = false
@ -71,7 +71,7 @@
new BBox([
[lon0, lat0],
[lon1, lat1],
]).pad(0.01),
]).pad(0.01)
)
if (perLayer !== undefined) {
const id = poi.osm_type + "/" + poi.osm_id
@ -102,7 +102,7 @@
</script>
<div class="normal-background flex justify-between rounded-full pl-2">
<form class="w-full flex flex-wrap">
<form class="flex w-full flex-wrap">
{#if isRunning}
<Loading>{Translations.t.general.search.searching}</Loading>
{:else}
@ -110,13 +110,16 @@
type="search"
class="w-full"
bind:this={inputElement}
on:keypress={(keypr) =>{ feedback = undefined; return (keypr.key === "Enter" ? performSearch() : undefined); }}
on:keypress={(keypr) => {
feedback = undefined
return keypr.key === "Enter" ? performSearch() : undefined
}}
bind:value={searchContents}
use:placeholder={Translations.t.general.search.search}
/>
{#if feedback !== undefined}
<!-- The feedback is _always_ shown for screenreaders and to make sure that the searchfield can still be selected by tabbing-->
<div class="alert " role="alert" aria-live="assertive">
<div class="alert" role="alert" aria-live="assertive">
{feedback}
</div>
{/if}

View file

@ -21,7 +21,7 @@
</script>
<a class="flex items-center" href={mapillaryLink} target="_blank">
<Mapillary_black class={twMerge("shrink-0",large ? "m-2 mr-4 h-12 w-12" : "w-6 h-6 pr-2")} />
<Mapillary_black class={twMerge("shrink-0", large ? "m-2 mr-4 h-12 w-12" : "h-6 w-6 pr-2")} />
{#if large}
<div class="flex flex-col">
<Tr t={Translations.t.general.attribution.openMapillary} />

View file

@ -13,8 +13,10 @@
export let hideTooltip = false
</script>
<MapControlButton arialabel={Translations.t.general.labels.background}
on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}>
<MapControlButton
arialabel={Translations.t.general.labels.background}
on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}
>
<Square3Stack3dIcon class="h-6 w-6" />
{#if !hideTooltip}
<Tr cls="mx-2" t={Translations.t.general.backgroundSwitch} />

View file

@ -1,52 +1,58 @@
<script lang="ts">/**
* Shows the current address when shaken
**/
import Motion from "../../Sensors/Motion"
import { Geocoding } from "../../Logic/Osm/Geocoding"
import type { MapProperties } from "../../Models/MapProperties"
import Hotkeys from "../Base/Hotkeys"
import Translations from "../i18n/Translations"
import Locale from "../i18n/Locale"
<script lang="ts">
/**
* Shows the current address when shaken
**/
import Motion from "../../Sensors/Motion"
import { Geocoding } from "../../Logic/Osm/Geocoding"
import type { MapProperties } from "../../Models/MapProperties"
import Hotkeys from "../Base/Hotkeys"
import Translations from "../i18n/Translations"
import Locale from "../i18n/Locale"
export let mapProperties: MapProperties
let lastDisplayed: Date = undefined
let currentLocation: string = undefined
export let mapProperties: MapProperties
let lastDisplayed: Date = undefined
let currentLocation: string = undefined
async function displayLocation() {
lastDisplayed = new Date()
let result = await Geocoding.reverse(
mapProperties.location.data,
mapProperties.zoom.data,
Locale.language.data
)
let properties = result.features[0].properties
currentLocation = properties.display_name
window.setTimeout(() => {
if(properties.display_name !== currentLocation){
async function displayLocation() {
lastDisplayed = new Date()
let result = await Geocoding.reverse(
mapProperties.location.data,
mapProperties.zoom.data,
Locale.language.data
)
let properties = result.features[0].properties
currentLocation = properties.display_name
window.setTimeout(() => {
if (properties.display_name !== currentLocation) {
return
}
currentLocation = undefined
}, 5000)
}
Motion.singleton.lastShakeEvent.addCallbackD((shaken) => {
if (lastDisplayed !== undefined && shaken.getTime() - lastDisplayed.getTime() < 2000) {
return
}
currentLocation = undefined
}, 5000)
}
Motion.singleton.lastShakeEvent.addCallbackD(shaken => {
if (lastDisplayed !== undefined && shaken.getTime() - lastDisplayed.getTime() < 2000) {
return
}
displayLocation()
})
Hotkeys.RegisterHotkey({ nomod: "q" },
Translations.t.hotkeyDocumentation.queryCurrentLocation,
() => {
displayLocation()
})
Hotkeys.RegisterHotkey(
{ nomod: "q" },
Translations.t.hotkeyDocumentation.queryCurrentLocation,
() => {
displayLocation()
}
)
Motion.singleton.startListening()
Motion.singleton.startListening()
</script>
{#if currentLocation}
<div role="alert" aria-live="assertive" class="normal-background rounded-full border-interactive px-2">
<div
role="alert"
aria-live="assertive"
class="normal-background border-interactive rounded-full px-2"
>
{currentLocation}
</div>
{/if}

View file

@ -13,7 +13,7 @@
export let layer: LayerConfig
export let selectedElement: Feature
let tags: UIEventSource<Record<string, string>> = state.featureProperties.getStore(
selectedElement.properties.id,
selectedElement.properties.id
)
$: {
tags = state.featureProperties.getStore(selectedElement.properties.id)
@ -37,7 +37,7 @@
class="no-weblate title-icons links-as-button mr-2 flex flex-row flex-wrap items-center gap-x-0.5 p-1 pt-0.5 sm:pt-1"
>
{#each layer.titleIcons as titleIconConfig}
{#if (titleIconConfig.condition?.matchesProperties($tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties({ ...$metatags, ...$tags }) ?? true) && titleIconConfig.IsKnown($tags)}
{#if (titleIconConfig.condition?.matchesProperties($tags) ?? true) && (titleIconConfig.metacondition?.matchesProperties( { ...$metatags, ...$tags } ) ?? true) && titleIconConfig.IsKnown($tags)}
<div class={titleIconConfig.renderIconClass ?? "flex h-8 w-8 items-center"}>
<TagRenderingAnswer
config={titleIconConfig}
@ -53,16 +53,18 @@
</div>
</div>
<button on:click={() => state.selectedElement.setData(undefined)}
use:ariaLabel={Translations.t.general.backToMap}
class="border-none p-0 rounded-full">
<button
on:click={() => state.selectedElement.setData(undefined)}
use:ariaLabel={Translations.t.general.backToMap}
class="rounded-full border-none p-0"
>
<XCircleIcon aria-hidden={true} class="h-8 w-8" />
</button>
</div>
{/if}
<style>
:global(.title-icons a) {
display: block !important;
}
:global(.title-icons a) {
display: block !important;
}
</style>

View file

@ -19,22 +19,23 @@
state.selectedElement.setData(feature)
}
let bearingAndDist: Store<{ bearing: number, dist: number }> = state.mapProperties.location.map(l => {
let bearingAndDist: Store<{ bearing: number; dist: number }> = state.mapProperties.location.map(
(l) => {
let fcenter = GeoOperations.centerpointCoordinates(feature)
let mapCenter = [l.lon, l.lat]
let bearing = Math.round(GeoOperations.bearing(fcenter, mapCenter))
let dist = Math.round(GeoOperations.distanceBetween(fcenter, mapCenter))
return { bearing, dist }
},
}
)
</script>
<div class="cursor-pointer small flex" on:click={() => select()}>
<div class="small flex cursor-pointer" on:click={() => select()}>
<span class="flex">
{#if i !== undefined}
<span class="font-bold">{i + 1}.</span>
{/if}
{#if i !== undefined}
<span class="font-bold">{i + 1}.</span>
{/if}
<TagRenderingAnswer config={layer.title} {layer} selectedElement={feature} {state} {tags} />
{$bearingAndDist.dist}m {$bearingAndDist.bearing}°
</span>

View file

@ -101,7 +101,7 @@
<If condition={state.featureSwitches.featureSwitchSearch}>
<div
class=".button low-interaction m-1 flex flex-wrap h-fit w-full items-center justify-end gap-x-2 gap-y-2 rounded border p-1"
class=".button low-interaction m-1 flex h-fit w-full flex-wrap items-center justify-end gap-x-2 gap-y-2 rounded border p-1"
>
<div style="min-width: 16rem; " class="grow">
<Geosearch
@ -117,7 +117,7 @@
</div>
<button
class={twJoin(
"flex shrink-0 w-fit items-center justify-between gap-x-2 small",
"small flex w-fit shrink-0 items-center justify-between gap-x-2",
!searchEnabled && "disabled"
)}
on:click={() => triggerSearch.ping()}

View file

@ -43,7 +43,6 @@
{/each}
</div>
{#if filteredThemes.length === 0}
<NoThemeResultButton {search} />
{/if}

View file

@ -21,13 +21,7 @@
</script>
{#if customThemes.length > 0}
<ThemesList
{search}
{state}
themes={customThemes}
isCustom={true}
hideThemes={false}
>
<ThemesList {search} {state} themes={customThemes} isCustom={true} hideThemes={false}>
<svelte:fragment slot="title">
<h3>
<Tr t={t.customThemeTitle} />

View file

@ -17,26 +17,27 @@
const t = Translations.t.general.visualFeedback
let centerFeatures = state.closestFeatures.features
let lastAction: UIEventSource<KeyNavigationEvent> = new UIEventSource<KeyNavigationEvent>(undefined)
let lastAction: UIEventSource<KeyNavigationEvent> = new UIEventSource<KeyNavigationEvent>(
undefined
)
state.mapProperties.onKeyNavigationEvent((event) => {
lastAction.setData(event)
})
lastAction.stabilized(750).addCallbackAndRunD(_ => lastAction.setData(undefined))
lastAction.stabilized(750).addCallbackAndRunD((_) => lastAction.setData(undefined))
</script>
<div aria-live="assertive" class="p-1" role="alert">
<div aria-live="assertive" class="p-1" role="alert">
{#if $lastAction !== undefined}
<Tr t={t[$lastAction.key]} />
{:else if $centerFeatures.length === 0}
<Tr t={t.noCloseFeatures} />
{:else}
<div class="pointer-events-auto">
<Tr t={t.closestFeaturesAre.Subs({n: $featuresInViewPort?.length})} />
<Tr t={t.closestFeaturesAre.Subs({ n: $featuresInViewPort?.length })} />
<ol class="list-none">
{#each $centerFeatures as feat, i (feat.properties.id)}
<li class="flex">
<Summary {state} feature={feat} {i}/>
<Summary {state} feature={feat} {i} />
</li>
{/each}
</ol>

View file

@ -9,6 +9,7 @@
let gotMeasurement = o.gotMeasurement
o.startMeasurements()
</script>
{#if !$gotMeasurement}
No device orientation data available
{:else}

View file

@ -19,16 +19,20 @@
</script>
<div class="relative">
<img bind:this={imgEl}
class={imgClass ?? ""}
class:cursor-pointer={previewedImage !== undefined}
on:click={() => {previewedImage?.setData(image)}}
on:error={(event) => {
if(fallbackImage){
imgEl.src = fallbackImage
}
}}
src={image.url}>
<img
bind:this={imgEl}
class={imgClass ?? ""}
class:cursor-pointer={previewedImage !== undefined}
on:click={() => {
previewedImage?.setData(image)
}}
on:error={(event) => {
if (fallbackImage) {
imgEl.src = fallbackImage
}
}}
src={image.url}
/>
<div class="absolute bottom-0 left-0">
<ImageAttribution {image} />

View file

@ -24,7 +24,9 @@
<div class="flex flex-col">
{#if $license.title}
{#if $license.informationLocation}
<a href={$license.informationLocation.href} target="_blank" rel="noopener nofollower">{$license.title}</a>
<a href={$license.informationLocation.href} target="_blank" rel="noopener nofollower">
{$license.title}
</a>
{:else}
$license.title
{/if}

View file

@ -62,7 +62,11 @@
<div class="flex w-fit shrink-0 flex-col">
<div class="cursor-zoom-in" on:click={() => state.previewedImage.setData(providedImage)}>
<AttributedImage image={providedImage} imgClass="max-h-64 w-auto" previewedImage="{state.previewedImage}"/>
<AttributedImage
image={providedImage}
imgClass="max-h-64 w-auto"
previewedImage={state.previewedImage}
/>
</div>
{#if linkable}
<label>

View file

@ -28,13 +28,14 @@
<LoginToggle {state}>
{#if expanded}
<NearbyImages {tags} {state} {lon} {lat} {feature} {linkable} {layer}>
<button slot="corner"
class="h-6 w-6 cursor-pointer no-image-background p-0 border-none"
use:ariaLabel={t.close}
on:click={() => {
expanded = false
}}>
<button
slot="corner"
class="no-image-background h-6 w-6 cursor-pointer border-none p-0"
use:ariaLabel={t.close}
on:click={() => {
expanded = false
}}
>
<XCircleIcon />
</button>
</NearbyImages>
@ -42,8 +43,8 @@
<button
class="flex w-full items-center"
on:click={() => {
expanded = true
}}
expanded = true
}}
aria-expanded={expanded}
>
<Camera_plus class="mr-2 block h-8 w-8 p-1" />

View file

@ -17,18 +17,16 @@
let featureBearing: number = 45
if (feature?.geometry?.type === "LineString") {
/* Bearing between -180 and + 180, positive is clockwise*/
featureBearing = Math.round(GeoOperations.bearing(
feature.geometry.coordinates[0],
feature.geometry.coordinates.at(-1),
))
featureBearing = Math.round(
GeoOperations.bearing(feature.geometry.coordinates[0], feature.geometry.coordinates.at(-1))
)
}
let previewDegrees: UIEventSource<string> = new UIEventSource<string>(undefined)
let previewPercentage: UIEventSource<string> = new UIEventSource<string>(undefined)
function degreesToPercentage(beta: number): string {
const perc = Math.tan(beta * Math.PI / 180) * 100
const perc = Math.tan((beta * Math.PI) / 180) * 100
const rounded = Math.round(perc / 2.5) * 2.5
return rounded + "%"
}
@ -40,7 +38,7 @@
let gotMeasurement = orientation.gotMeasurement
let valuesign = alpha.map(phoneBearing => {
let valuesign = alpha.map((phoneBearing) => {
if (featureBearing === undefined) {
return 1
}
@ -56,31 +54,30 @@
} else {
return -1
}
})
beta.map(beta => {
// As one moves forward on a way, a positive incline gets higher, and a negative incline gets lower.
let valueSign = valuesign.data
beta.map(
(beta) => {
// As one moves forward on a way, a positive incline gets higher, and a negative incline gets lower.
let valueSign = valuesign.data
if (mode === "degrees") {
value.setData(valueSign * beta + "°")
} else {
value.setData(degreesToPercentage(valueSign * beta))
}
previewDegrees.setData(beta + "°")
previewPercentage.setData(degreesToPercentage(beta))
}, [valuesign, beta])
if (mode === "degrees") {
value.setData(valueSign * beta + "°")
} else {
value.setData(degreesToPercentage(valueSign * beta))
}
previewDegrees.setData(beta + "°")
previewPercentage.setData(degreesToPercentage(beta))
},
[valuesign, beta]
)
</script>
{#if $gotMeasurement}
<div class="flex flex-col m-2">
<div class="flex w-full">
<div class="font-bold w-full flex justify-around items-center text-5xl">
{#if $gotMeasurement}
<div class="m-2 flex flex-col">
<div class="flex w-full">
<div class="flex w-full items-center justify-around text-5xl font-bold">
<div>
{$previewDegrees}
</div>
@ -88,7 +85,6 @@
{$previewPercentage}
</div>
</div>
</div>
<div>
@ -96,14 +92,14 @@
</div>
<If condition={state?.featureSwitchIsTesting ?? new ImmutableStore(true)}>
<span class="subtle">
Way: {featureBearing}°, compass: {$alpha}°, diff: {(featureBearing - $alpha)}
{#if $valuesign === 1}
Forward
{:else}
Backward
{/if}
</span>
<span class="subtle">
Way: {featureBearing}°, compass: {$alpha}°, diff: {featureBearing - $alpha}
{#if $valuesign === 1}
Forward
{:else}
Backward
{/if}
</span>
</If>
</div>
{/if}

View file

@ -39,8 +39,8 @@
{#if availableLanguages?.length > 1}
<form class={twMerge("flex max-w-full items-center pr-4", clss)}>
<label class="flex neutral-label" use:ariaLabel={Translations.t.general.pickLanguage}>
<LanguageIcon class="h-4 w-4 mr-1 shrink-0" aria-hidden="true" />
<label class="neutral-label flex" use:ariaLabel={Translations.t.general.pickLanguage}>
<LanguageIcon class="mr-1 h-4 w-4 shrink-0" aria-hidden="true" />
<Dropdown cls="max-w-full" value={assignTo}>
{#if preferredFiltered}
{#each preferredFiltered as language}
@ -54,12 +54,12 @@
<option disabled />
{/if}
{#each availableLanguages.filter(l => l !== "_context") as language}
{#each availableLanguages.filter((l) => l !== "_context") as language}
<option value={language} class="font-bold">
{native[language] ?? ""}
{#if language !== $current}
{#if language_translations[language]?.[$current] !== undefined}
({ language_translations[language]?.[$current] + " - " + language ?? language})
({language_translations[language]?.[$current] + " - " + language ?? language})
{:else}
({language})
{/if}

View file

@ -1,102 +1,102 @@
<script lang="ts">
import { UIEventSource } from "../../Logic/UIEventSource";
import type { ValidatorType } from "./Validators";
import Validators from "./Validators";
import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid";
import { Translation } from "../i18n/Translation";
import { createEventDispatcher, onDestroy } from "svelte";
import { Validator } from "./Validator";
import { Unit } from "../../Models/Unit";
import UnitInput from "../Popup/UnitInput.svelte";
import { Utils } from "../../Utils";
import { twMerge } from "tailwind-merge";
import { UIEventSource } from "../../Logic/UIEventSource"
import type { ValidatorType } from "./Validators"
import Validators from "./Validators"
import { ExclamationIcon } from "@rgossiaux/svelte-heroicons/solid"
import { Translation } from "../i18n/Translation"
import { createEventDispatcher, onDestroy } from "svelte"
import { Validator } from "./Validator"
import { Unit } from "../../Models/Unit"
import UnitInput from "../Popup/UnitInput.svelte"
import { Utils } from "../../Utils"
import { twMerge } from "tailwind-merge"
export let type: ValidatorType;
export let feedback: UIEventSource<Translation> | undefined = undefined;
export let cls: string = undefined;
export let getCountry: () => string | undefined;
export let placeholder: string | Translation | undefined;
export let unit: Unit = undefined;
export let type: ValidatorType
export let feedback: UIEventSource<Translation> | undefined = undefined
export let cls: string = undefined
export let getCountry: () => string | undefined
export let placeholder: string | Translation | undefined
export let unit: Unit = undefined
/**
* Valid state, exported to the calling component
*/
export let value: UIEventSource<string | undefined>;
export let value: UIEventSource<string | undefined>
/**
* Internal state bound to the input element.
*
* This is only copied to 'value' when appropriate so that no invalid values leak outside;
* Additionally, the unit is added when copying
*/
let _value = new UIEventSource(value.data ?? "");
let _value = new UIEventSource(value.data ?? "")
let validator: Validator = Validators.get(type ?? "string");
let validator: Validator = Validators.get(type ?? "string")
if (validator === undefined) {
console.warn("Didn't find a validator for type", type);
console.warn("Didn't find a validator for type", type)
}
let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined);
let _placeholder = placeholder ?? validator?.getPlaceholder() ?? type;
let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined)
let _placeholder = placeholder ?? validator?.getPlaceholder() ?? type
function initValueAndDenom() {
if (unit && value.data) {
const [v, denom] = unit?.findDenomination(value.data, getCountry);
const [v, denom] = unit?.findDenomination(value.data, getCountry)
if (denom) {
_value.setData(v);
selectedUnit.setData(denom.canonical);
_value.setData(v)
selectedUnit.setData(denom.canonical)
} else {
_value.setData(value.data ?? "");
_value.setData(value.data ?? "")
}
} else {
_value.setData(value.data ?? "");
_value.setData(value.data ?? "")
}
}
initValueAndDenom();
initValueAndDenom()
$: {
// The type changed -> reset some values
validator = Validators.get(type ?? "string");
validator = Validators.get(type ?? "string")
_placeholder = placeholder ?? validator?.getPlaceholder() ?? type;
feedback?.setData(validator?.getFeedback(_value.data, getCountry));
_placeholder = placeholder ?? validator?.getPlaceholder() ?? type
feedback?.setData(validator?.getFeedback(_value.data, getCountry))
initValueAndDenom();
initValueAndDenom()
}
function setValues() {
// Update the value stores
const v = _value.data;
const v = _value.data
if (!validator?.isValid(v, getCountry) || v === "") {
feedback?.setData(validator?.getFeedback(v, getCountry));
value.setData("");
return;
feedback?.setData(validator?.getFeedback(v, getCountry))
value.setData("")
return
}
if (unit !== undefined && isNaN(Number(v))) {
value.setData(undefined);
return;
value.setData(undefined)
return
}
feedback?.setData(undefined);
feedback?.setData(undefined)
if (selectedUnit.data) {
value.setData(unit.toOsm(v, selectedUnit.data))
} else {
value.setData(v);
value.setData(v)
}
}
onDestroy(_value.addCallbackAndRun((_) => setValues()));
onDestroy(_value.addCallbackAndRun((_) => setValues()))
if (unit === undefined) {
onDestroy(
value.addCallbackAndRunD((fromUpstream) => {
if (_value.data !== fromUpstream && fromUpstream !== "") {
_value.setData(fromUpstream);
_value.setData(fromUpstream)
}
})
);
}else{
// Handled by the UnitInput
)
} else {
// Handled by the UnitInput
}
onDestroy(selectedUnit.addCallback((_) => setValues()));
onDestroy(selectedUnit.addCallback((_) => setValues()))
if (validator === undefined) {
throw (
"Not a valid type (no validator found) for type '" +
@ -109,17 +109,17 @@
)
.slice(0, 5)
.join(", ")
);
)
}
const isValid = _value.map((v) => validator?.isValid(v, getCountry) ?? true);
const isValid = _value.map((v) => validator?.isValid(v, getCountry) ?? true)
let htmlElem: HTMLInputElement;
let htmlElem: HTMLInputElement
let dispatch = createEventDispatcher<{ selected; submit }>();
let dispatch = createEventDispatcher<{ selected; submit }>()
$: {
if (htmlElem !== undefined) {
htmlElem.onfocus = () => dispatch("selected");
htmlElem.onfocus = () => dispatch("selected")
}
}
@ -128,9 +128,9 @@
*/
function sendSubmit() {
if (feedback?.data) {
console.log("Not sending a submit as there is feedback");
console.log("Not sending a submit as there is feedback")
}
dispatch("submit");
dispatch("submit")
}
</script>

View file

@ -48,11 +48,11 @@
window.requestAnimationFrame(() => {
_map.resize()
})
_map.on("load", function() {
_map.on("load", function () {
_map.resize()
const canvas = _map.getCanvas()
ariaLabel(canvas, Translations.t.general.visualFeedback.navigation)
canvas.role="application"
canvas.role = "application"
canvas.tabIndex = 0
})
map.set(_map)
@ -62,16 +62,10 @@
if (_map) _map.remove()
map = null
})
</script>
<svelte:head>
<link href="./maplibre-gl.css" rel="stylesheet" />
</svelte:head>
<div
bind:this={container}
class="map relative top-0 left-0 w-full h-full"
id="map"
/>
<div bind:this={container} class="map relative top-0 left-0 h-full w-full" id="map" />

View file

@ -26,13 +26,19 @@
<LoginToggle ignoreLoading={true} {state}>
{#if $isFavourite}
<button class="p-0 m-0 h-8 w-8" on:click={() => markFavourite(false)}
use:ariaLabel={Translations.t.favouritePoi.button.isMarkedShort}>
<button
class="m-0 h-8 w-8 p-0"
on:click={() => markFavourite(false)}
use:ariaLabel={Translations.t.favouritePoi.button.isMarkedShort}
>
<HeartSolidIcon aria-hidden={true} />
</button>
{:else}
<button class="p-0 m-0 h-8 w-8 no-image-background soft" on:click={() => markFavourite(true)}
use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}>
<button
class="no-image-background soft m-0 h-8 w-8 p-0"
on:click={() => markFavourite(true)}
use:ariaLabel={Translations.t.favouritePoi.button.isNotMarkedShort}
>
<HeartOutlineIcon aria-hidden={true} />
</button>
{/if}

View file

@ -119,12 +119,18 @@
{/if}
<div class="flex flex-wrap">
<If condition={currentMapProperties.zoom.mapD(zoom => zoom >= Constants.minZoomLevelToAddNewPoint)}>
<button class="flex primary w-full"
on:click={() => {
moveWizardState.moveFeature(newLocation.data, reason.data, featureToMove);
currentStep = "moved"
}}>
<If
condition={currentMapProperties.zoom.mapD(
(zoom) => zoom >= Constants.minZoomLevelToAddNewPoint
)}
>
<button
class="primary flex w-full"
on:click={() => {
moveWizardState.moveFeature(newLocation.data, reason.data, featureToMove)
currentStep = "moved"
}}
>
<Move class="mr-2 h-6 w-6" />
<Tr t={t.confirmMove} />
</button>
@ -148,8 +154,12 @@
{:else if currentStep === "moved"}
<div class="flex flex-col">
<Tr cls="thanks" t={t.pointIsMoved} />
<button on:click={() => {currentStep = "reason"}}>
<Move class="w-6 h-6 pr-2" />
<button
on:click={() => {
currentStep = "reason"
}}
>
<Move class="h-6 w-6 pr-2" />
<Tr t={t.inviteToMoveAgain} />
</button>
</div>

View file

@ -17,8 +17,8 @@
export let layer: LayerConfig
export let config: TagRenderingConfig
export let extraClasses: string | undefined = undefined
export let id : string = undefined
export let id: string = undefined
if (config === undefined) {
throw "Config is undefined in tagRenderingAnswer"

View file

@ -75,13 +75,20 @@
onDestroy(highlightedRendering?.addCallbackAndRun(() => setHighlighting()))
onDestroy(_htmlElement.addCallbackAndRun(() => setHighlighting()))
}
let answerId = "answer-"+Utils.randomString(5)
let answerId = "answer-" + Utils.randomString(5)
</script>
<div bind:this={htmlElem} class={twMerge(clss, "tr-" + config.id)}>
{#if config.question && (!editingEnabled || $editingEnabled)}
{#if editMode}
<TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer} on:saved={() => editMode = false}>
<TagRenderingQuestion
{config}
{tags}
{selectedElement}
{state}
{layer}
on:saved={() => (editMode = false)}
>
<button
slot="cancel"
class="secondary"
@ -104,7 +111,7 @@
</TagRenderingQuestion>
{:else}
<div class="low-interaction flex items-center justify-between overflow-hidden rounded px-2">
<TagRenderingAnswer id={answerId} {config} {tags} {selectedElement} {state} {layer} />
<TagRenderingAnswer id={answerId} {config} {tags} {selectedElement} {state} {layer} />
<button
on:click={() => {
editMode = true

View file

@ -30,7 +30,7 @@
{#if mapping.icon !== undefined}
<div class="inline-flex items-center">
<Icon icon={mapping.icon} clss={twJoin(`mapping-icon-${mapping.iconClass}`, "mx-2")}/>
<Icon icon={mapping.icon} clss={twJoin(`mapping-icon-${mapping.iconClass}`, "mx-2")} />
<SpecialTranslation t={mapping.then} {tags} {state} {layer} feature={selectedElement} />
</div>
{:else if mapping.then !== undefined}

View file

@ -239,8 +239,12 @@
{#if config.mappings?.length >= 8}
<div class="sticky flex w-full" aria-hidden="true">
<Search class="h-6 w-6" />
<input type="text" bind:value={$searchTerm} class="w-full"
use:placeholder={Translations.t.general.searchAnswer} />
<input
type="text"
bind:value={$searchTerm}
class="w-full"
use:placeholder={Translations.t.general.searchAnswer}
/>
</div>
{/if}

View file

@ -1,67 +1,67 @@
<script lang="ts">
import { Unit } from "../../Models/Unit";
import { Store, UIEventSource } from "../../Logic/UIEventSource";
import Tr from "../Base/Tr.svelte";
import { onDestroy, onMount } from "svelte";
import { Denomination } from "../../Models/Denomination";
import { Unit } from "../../Models/Unit"
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import Tr from "../Base/Tr.svelte"
import { onDestroy, onMount } from "svelte"
import { Denomination } from "../../Models/Denomination"
export let unit: Unit;
export let unit: Unit
/**
* The current value of the input field
* Not necessarily a correct number, should not contain the denomination
*/
export let textValue: UIEventSource<string>;
export let textValue: UIEventSource<string>
/**
* The actual _valid_ value that is upstreamed, including the denomination
*/
export let upstreamValue: Store<string>;
export let upstreamValue: Store<string>
let isSingle: Store<boolean> = textValue.map((v) => Number(v) === 1);
let isSingle: Store<boolean> = textValue.map((v) => Number(v) === 1)
export let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined)
export let getCountry = () => "?"
export let selectedUnit: UIEventSource<string> = new UIEventSource<string>(undefined);
export let getCountry = () => "?";
onMount(() => {
console.log("Setting selected unit based on country", getCountry(), upstreamValue.data)
if(upstreamValue.data === undefined || upstreamValue.data === ""){
if (upstreamValue.data === undefined || upstreamValue.data === "") {
// Init the selected unit
let denomination: Denomination = unit.getDefaultDenomination(getCountry);
let denomination: Denomination = unit.getDefaultDenomination(getCountry)
console.log("Found denom", denomination.canonical)
selectedUnit.setData(denomination.canonical)
}
})
onDestroy(
upstreamValue.addCallbackAndRun((v) => {
if(v === undefined || v === ""){
if (v === undefined || v === "") {
return
}
let denomination: Denomination = unit.getDefaultDenomination(getCountry);
const selected = unit.findDenomination(v, getCountry);
if(selected){
denomination = selected[1];
let denomination: Denomination = unit.getDefaultDenomination(getCountry)
const selected = unit.findDenomination(v, getCountry)
if (selected) {
denomination = selected[1]
}
selectedUnit.setData(denomination.canonical);
selectedUnit.setData(denomination.canonical)
})
);
)
onDestroy(
textValue.addCallbackAndRunD((v) => {
// Fallback in case that the user manually types a denomination
const [value, denomination] = unit.findDenomination(v, getCountry);
const [value, denomination] = unit.findDenomination(v, getCountry)
if (value === undefined || denomination === undefined) {
return;
return
}
if(value === v){
if (value === v) {
// The input value actually didn't have a denomination typed out - so lets ignore this one
// If a denomination is given, it is the default value anyway
return;
return
}
textValue.setData(value);
selectedUnit.setData(denomination.canonical);
textValue.setData(value)
selectedUnit.setData(denomination.canonical)
})
);
)
</script>
<select bind:value={$selectedUnit}>
@ -70,7 +70,7 @@
{#if $isSingle}
<Tr t={denom.humanSingular} />
{:else}
<Tr t={denom.human.Subs({quantity: ""})} />
<Tr t={denom.human.Subs({ quantity: "" })} />
{/if}
</option>
{/each}

View file

@ -7,15 +7,15 @@
import Add from "../assets/svg/Add.svelte"
</script>
<div class="flex flex-col p-4 h-screen overflow-hidden">
<div class="flex h-screen flex-col overflow-hidden p-4">
<h2 class="flex items-center">
<EyeIcon class="w-6 pr-2" />
<Tr t={Translations.t.privacy.title} />
</h2>
<div class="overflow-auto h-full border border-gray-500 p-4">
<div class="h-full overflow-auto border border-gray-500 p-4">
<PrivacyPolicy />
</div>
<a class="flex button" href={Utils.HomepageLink()}>
<a class="button flex" href={Utils.HomepageLink()}>
<Add class="h-6 w-6" />
<Tr t={Translations.t.general.backToIndex} />
</a>

View file

@ -4,13 +4,13 @@
import { Store, Stores } from "../Logic/UIEventSource"
let maxAcc = Motion.singleton.maxAcc
let shaken =Motion.singleton.lastShakeEvent
let recentlyShaken = Stores.Chronic(250).mapD(now => now.getTime() - 3000 < shaken.data?.getTime())
let shaken = Motion.singleton.lastShakeEvent
let recentlyShaken = Stores.Chronic(250).mapD(
(now) => now.getTime() - 3000 < shaken.data?.getTime()
)
</script>
Acc: {$maxAcc}
{#if $recentlyShaken}
<div class="text-red-500 text-5xl">
SHAKEN
</div>
{/if}
<div class="text-5xl text-red-500">SHAKEN</div>
{/if}

View file

@ -13,7 +13,13 @@
import type { MapProperties } from "../Models/MapProperties"
import Geosearch from "./BigComponents/Geosearch.svelte"
import Translations from "./i18n/Translations"
import { CogIcon, EyeIcon, HeartIcon, MenuIcon, XCircleIcon } from "@rgossiaux/svelte-heroicons/solid"
import {
CogIcon,
EyeIcon,
HeartIcon,
MenuIcon,
XCircleIcon,
} from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
@ -75,14 +81,11 @@
let maplibremap: UIEventSource<MlMap> = state.map
let selectedElement: UIEventSource<Feature> = new UIEventSource<Feature>(undefined)
let compass = Orientation.singleton.alpha
let compassLoaded = Orientation.singleton.gotMeasurement
Orientation.singleton.startMeasurements()
state.selectedElement.addCallback((selected) => {
if (!selected) {
selectedElement.setData(selected)
return
@ -93,20 +96,20 @@
}
// ... we give svelte some time to update with requestAnimationFrame ...
window.requestAnimationFrame(() => {
// ... and we force a fresh popup window
// ... and we force a fresh popup window
selectedElement.setData(selected)
})
})
let selectedLayer: Store<LayerConfig> = state.selectedElement.mapD((element) =>
state.layout.getMatchingLayer(element.properties),
state.layout.getMatchingLayer(element.properties)
)
let currentZoom = state.mapProperties.zoom
let showCrosshair = state.userRelatedState.showCrosshair
let visualFeedback = state.visualFeedback
let viewport: UIEventSource<HTMLDivElement> = new UIEventSource<HTMLDivElement>(undefined)
let featuresInViewPort: UIEventSource<Feature[]> = new UIEventSource<Feature[]>(undefined)
viewport.addCallbackAndRunD(viewport => {
viewport.addCallbackAndRunD((viewport) => {
state.featuresInView.features.addCallbackAndRunD((features: Feature[]) => {
const rect = viewport.getBoundingClientRect()
const mlmap = state.map.data
@ -115,17 +118,19 @@
}
const topLeft = mlmap.unproject([rect.left, rect.top])
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
const bbox = new BBox([[topLeft.lng, topLeft.lat], [bottomRight.lng, bottomRight.lat]])
const bbox = new BBox([
[topLeft.lng, topLeft.lat],
[bottomRight.lng, bottomRight.lat],
])
const bboxGeo = bbox.asGeoJson({})
console.log("BBOX:", bboxGeo)
const filtered = features.filter((f: Feature) => {
console.log(f, bboxGeo)
return GeoOperations.calculateOverlap(bboxGeo, [f]).length > 0
})
featuresInViewPort.setData(filtered)
})
})
let mapproperties: MapProperties = state.mapProperties
let featureSwitches: FeatureSwitchState = state.featureSwitches
@ -137,7 +142,7 @@
onDestroy(
rasterLayer.addCallbackAndRunD((l) => {
rasterLayerName = l.properties.name
}),
})
)
let previewedImage = state.previewedImage
@ -159,9 +164,10 @@
</div>
{#if $visualFeedback}
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden flex items-center justify-center">
<div bind:this={$viewport} style="border: 2px solid #ff000044; width: 300px; height: 300px"></div>
<div
class="absolute top-0 left-0 flex h-screen w-screen items-center justify-center overflow-hidden"
>
<div bind:this={$viewport} style="border: 2px solid #ff000044; width: 300px; height: 300px" />
</div>
{/if}
@ -171,15 +177,19 @@
<div class="pointer-events-auto float-right mt-1 px-1 max-[480px]:w-full sm:m-2">
<Geosearch
bounds={state.mapProperties.bounds}
on:searchCompleted={() => {state.map?.data?.getCanvas()?.focus()}}
on:searchCompleted={() => {
state.map?.data?.getCanvas()?.focus()
}}
perLayer={state.perLayer}
selectedElement={state.selectedElement}
/>
</div>
</If>
<div class="float-left m-1 flex flex-col sm:mt-2">
<MapControlButton on:click={() => state.guistate.themeIsOpened.setData(true)}
on:keydown={forwardEventToMap}>
<MapControlButton
on:click={() => state.guistate.themeIsOpened.setData(true)}
on:keydown={forwardEventToMap}
>
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
<img class="mr-0.5 block h-6 w-6 sm:mr-1 md:mr-2 md:h-8 md:w-8" src={layout.icon} />
<b class="mr-1">
@ -215,7 +225,7 @@
<div class="alert w-fit">Testmode</div>
</If>
</div>
<div class="flex flex-col w-full justify-center items-center">
<div class="flex w-full flex-col items-center justify-center">
<!-- Flex and w-full are needed for the positioning -->
<!-- Centermessage -->
<StateIndicator {state} />
@ -248,9 +258,10 @@
<div class="flex">
<!-- bottom left elements -->
<If condition={state.featureSwitches.featureSwitchFilter}>
<MapControlButton arialabel={Translations.t.general.labels.filter}
on:click={() => state.guistate.openFilterView()}
on:keydown={forwardEventToMap}
<MapControlButton
arialabel={Translations.t.general.labels.filter}
on:click={() => state.guistate.openFilterView()}
on:keydown={forwardEventToMap}
>
<Filter class="h-6 w-6" />
</MapControlButton>
@ -284,41 +295,44 @@
/>
</div>
</If>
<MapControlButton arialabel={Translations.t.general.labels.zoomIn}
on:click={() => mapproperties.zoom.update((z) => z + 1)}
on:keydown={forwardEventToMap}
<MapControlButton
arialabel={Translations.t.general.labels.zoomIn}
on:click={() => mapproperties.zoom.update((z) => z + 1)}
on:keydown={forwardEventToMap}
>
<Plus class="h-8 w-8" />
</MapControlButton>
<MapControlButton arialabel={Translations.t.general.labels.zoomOut}
on:click={() => mapproperties.zoom.update((z) => z - 1)}
on:keydown={forwardEventToMap}
<MapControlButton
arialabel={Translations.t.general.labels.zoomOut}
on:click={() => mapproperties.zoom.update((z) => z - 1)}
on:keydown={forwardEventToMap}
>
<Min class="h-8 w-8" />
</MapControlButton>
<If condition={featureSwitches.featureSwitchGeolocation}>
<div class="relative m-0">
<MapControlButton arialabel={Translations.t.general.labels.jumpToLocation}
on:click={() => state.geolocationControl.handleClick()}
on:keydown={forwardEventToMap}
<MapControlButton
arialabel={Translations.t.general.labels.jumpToLocation}
on:click={() => state.geolocationControl.handleClick()}
on:keydown={forwardEventToMap}
>
<GeolocationControl {state} /> <!-- h-8 w-8 + p-0.5 sm:p-1 + 2px border => 9 sm: 10 in total-->
<GeolocationControl {state} />
<!-- h-8 w-8 + p-0.5 sm:p-1 + 2px border => 9 sm: 10 in total-->
</MapControlButton>
{#if $compassLoaded}
<div class="absolute top-0 left-0 w-0 h-0 m-0.5 sm:m-1">
<Compass_arrow class="compass_arrow"
style={`rotate: calc(${-$compass}deg + 45deg); transform-origin: 50% 50%;`} />
<div class="absolute top-0 left-0 m-0.5 h-0 w-0 sm:m-1">
<Compass_arrow
class="compass_arrow"
style={`rotate: calc(${-$compass}deg + 45deg); transform-origin: 50% 50%;`}
/>
</div>
{/if}
</div>
</If>
</div>
</div>
</div>
<LoginToggle ignoreLoading={true} {state}>
{#if ($showCrosshair === "yes" && $currentZoom >= 17) || $showCrosshair === "always" || $visualFeedback}
<div
@ -327,7 +341,8 @@
<Cross class="h-4 w-4" />
</div>
{/if}
<svelte:fragment slot="error" /> <!-- Add in an empty container to remove errors -->
<svelte:fragment slot="error" />
<!-- Add in an empty container to remove errors -->
</LoginToggle>
<If condition={state.previewedImage.map((i) => i !== undefined)}>
@ -365,7 +380,7 @@
selectedElement.setData(undefined)
}}
>
<div class="h-full w-full flex">
<div class="flex h-full w-full">
<SelectedElementView {state} layer={$selectedLayer} selectedElement={$selectedElement} />
</div>
</FloatOver>
@ -428,7 +443,6 @@
</FloatOver>
</If>
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
<!-- background layer selector -->
<FloatOver
@ -448,7 +462,6 @@
</FloatOver>
</IfHidden>
<If condition={state.guistate.menuIsOpened}>
<!-- Menu page -->
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
@ -491,22 +504,25 @@
<Tr t={Translations.t.general.attribution.donate} />
</a>
<button class="small soft flex" on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}>
<button
class="small soft flex"
on:click={() => state.guistate.communityIndexPanelIsOpened.setData(true)}
>
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</button>
<If condition={featureSwitches.featureSwitchEnableLogin}>
<OpenIdEditor mapProperties={state.mapProperties} />
<OpenJosm {state} />
<MapillaryLink large={false} mapProperties={state.mapProperties} />
</If>
<button class="small soft flex"
on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}
<button
class="small soft flex"
on:click={() => state.guistate.privacyPanelIsOpened.setData(true)}
>
<EyeIcon class="w-6 h-6 pr-1" />
<EyeIcon class="h-6 w-6 pr-1" />
<Tr t={Translations.t.privacy.title} />
</button>
<div class="m-2 flex flex-col">
@ -553,16 +569,14 @@
</h3>
<Favourites {state} />
</div>
</TabbedGroup>
</FloatOver>
</If>
<If condition={state.guistate.privacyPanelIsOpened}>
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
<div class="h-full flex flex-col overflow-hidden">
<h2 class="flex items-center low-interaction p-4 m-0">
<div class="flex h-full flex-col overflow-hidden">
<h2 class="low-interaction m-0 flex items-center p-4">
<EyeIcon class="w-6 pr-2" />
<Tr t={Translations.t.privacy.title} />
</h2>
@ -573,11 +587,10 @@
</FloatOver>
</If>
<If condition={state.guistate.communityIndexPanelIsOpened}>
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
<div class="h-full flex flex-col overflow-hidden">
<h2 class="flex items-center low-interaction p-4 m-0">
<div class="flex h-full flex-col overflow-hidden">
<h2 class="low-interaction m-0 flex items-center p-4">
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</h2>
@ -585,8 +598,5 @@
<CommunityIndexView location={state.mapProperties.location} />
</div>
</div>
</FloatOver>
</If>

View file

@ -1,7 +1,7 @@
{
"contributors": [
{
"commits": 6533,
"commits": 6578,
"contributor": "Pieter Vander Vennet"
},
{
@ -20,6 +20,10 @@
"commits": 33,
"contributor": "Christian Neumann"
},
{
"commits": 31,
"contributor": "Hosted Weblate"
},
{
"commits": 31,
"contributor": "Andrews Leruth"
@ -28,10 +32,6 @@
"commits": 31,
"contributor": "Pieter Fiers"
},
{
"commits": 30,
"contributor": "Hosted Weblate"
},
{
"commits": 30,
"contributor": "paunofu"

View file

@ -12,7 +12,7 @@
"gl": "lingua galega",
"he": "עברית",
"hu": "magyar",
"id": "Indonesia",
"id": "bahasa Indonesia",
"it": "italiano",
"ja": "日本語",
"nb_NO": "bokmål",
@ -23,5 +23,6 @@
"ru": "русский язык",
"sl": "slovenščina",
"sv": "svenska",
"zh_Hant": "簡體中文"
"zh_Hans": "简体中文",
"zh_Hant": "繁體中文"
}

View file

@ -146,7 +146,7 @@
"gl": "Lingua adigue",
"he": "אדיגית",
"hu": "adigei",
"id": "Adyghe",
"id": "bahasa Adyghe",
"it": "adighè",
"ja": "アディゲ語",
"nb_NO": "adygeisk",
@ -603,7 +603,7 @@
"gl": "árabe",
"he": "ערבית",
"hu": "arab",
"id": "Arab",
"id": "bahasa Arab",
"it": "arabo",
"ja": "アラビア語",
"nb_NO": "arabisk",
@ -929,7 +929,7 @@
"fi": "Awadhin kieli",
"fr": "awadhi",
"gl": "Lingua awadhi",
"he": "אוודית",
"he": "אוודהית",
"id": "Bahasa Awadhi",
"it": "awadhi",
"ja": "アワディー語",
@ -1606,7 +1606,7 @@
"gl": "lingua bretoa",
"he": "ברטונית",
"hu": "breton",
"id": "Breton",
"id": "Bahasa Breton",
"it": "bretone",
"ja": "ブルトン語",
"nb_NO": "bretonsk",
@ -1775,7 +1775,7 @@
"gl": "Lingua buriata",
"he": "בוריאטית",
"hu": "burját",
"id": "Buryat",
"id": "bahasa Buryat",
"it": "buriato",
"ja": "ブリヤート語",
"nb_NO": "burjatisk",
@ -2319,7 +2319,7 @@
"gl": "Lingua tártara de Crimea",
"he": "טטרית של קרים",
"hu": "krími tatár",
"id": "Tatar Krimea",
"id": "Bahasa Tatar Krimea",
"it": "tataro di Crimea",
"ja": "クリミア・タタール語",
"nb_NO": "krimtatarisk",
@ -2445,7 +2445,6 @@
"id": "Bahasa Chittagonia",
"it": "lingua chittagonian",
"ja": "チッタゴン語",
"nb_NO": "Chittagong",
"pl": "Język chatgaya",
"pt": "Língua chittagong",
"pt_BR": "Língua chittagong",
@ -2536,7 +2535,7 @@
"gl": "lingua dinamarquesa",
"he": "דנית",
"hu": "dán",
"id": "Denmark",
"id": "bahasa Denmark",
"it": "danese",
"ja": "デンマーク語",
"nb_NO": "dansk",
@ -2599,7 +2598,7 @@
"gl": "lingua alemá",
"he": "גרמנית",
"hu": "német",
"id": "Jerman",
"id": "bahasa Jerman",
"it": "tedesco",
"ja": "ドイツ語",
"nb_NO": "tysk",
@ -2970,8 +2969,8 @@
"ru": "новогреческий язык",
"sl": "novogrščina",
"sv": "nygrekiska",
"zh_Hans": "现代希腊语",
"zh_Hant": "現代希臘語",
"zh_Hans": "希腊语",
"zh_Hant": "希臘語",
"_meta": {
"countries": [
"CY",
@ -3591,7 +3590,7 @@
"gl": "lingua feroesa",
"he": "פארואזית",
"hu": "feröeri",
"id": "Faroe",
"id": "bahasa Faroe",
"it": "faroese",
"ja": "フェロー語",
"nb_NO": "færøysk",
@ -4878,7 +4877,7 @@
"gl": "lingua indonesia",
"he": "אינדונזית",
"hu": "indonéz",
"id": "Indonesia",
"id": "bahasa Indonesia",
"it": "indonesiano",
"ja": "インドネシア語",
"nb_NO": "indonesisk",
@ -5025,7 +5024,7 @@
"gl": "lingua islandesa",
"he": "איסלנדית",
"hu": "izlandi",
"id": "Islandia",
"id": "bahasa Islandia",
"it": "islandese",
"ja": "アイスランド語",
"nb_NO": "islandsk",
@ -5061,7 +5060,7 @@
"gl": "lingua italiana",
"he": "איטלקית",
"hu": "olasz",
"id": "Italia",
"id": "bahasa Italia",
"it": "italiano",
"ja": "イタリア語",
"nb_NO": "italiensk",
@ -5133,7 +5132,7 @@
"gl": "lingua xaponesa",
"he": "יפנית",
"hu": "japán",
"id": "Jepang",
"id": "bahasa Jepang",
"it": "giapponese",
"ja": "日本語",
"nb_NO": "japansk",
@ -5216,7 +5215,7 @@
"gl": "Lingua xavanesa",
"he": "ג'אווה",
"hu": "jávai",
"id": "Jawa",
"id": "bahasa Jawa",
"it": "giavanese",
"ja": "ジャワ語",
"nb_NO": "javanesisk",
@ -5253,7 +5252,7 @@
"gl": "lingua xeorxiana",
"he": "גאורגית",
"hu": "grúz",
"id": "Georgia",
"id": "Bahasa Georgia",
"it": "georgiano",
"ja": "ジョージア語",
"nb_NO": "georgisk",
@ -5288,7 +5287,7 @@
"gl": "Lingua karakalpak",
"he": "קראקלפקית",
"hu": "karakalpak",
"id": "Karakalpak",
"id": "Bahasa Karakalpak",
"it": "karakalpako",
"ja": "カラカルパク語",
"nl": "Karakalpaks",
@ -5472,7 +5471,6 @@
"ja": "カインガング語",
"nb_NO": "Kaingang",
"nl": "Kaingang",
"pl": "Języki caingang",
"pt": "Língua caingangue",
"pt_BR": "Língua kaingáng",
"ru": "Каинганг",
@ -5643,7 +5641,7 @@
"gl": "Lingua casaca",
"he": "קזחית",
"hu": "kazak",
"id": "Kazakh",
"id": "bahasa Kazakh",
"it": "kazako",
"ja": "カザフ語",
"nb_NO": "kasakhisk",
@ -5680,7 +5678,7 @@
"gl": "Lingua grenlandesa",
"he": "גרינלנדית",
"hu": "grönlandi",
"id": "Greenland",
"id": "bahasa Greenland",
"it": "groenlandese",
"ja": "グリーンランド語",
"nb_NO": "grønlandsk",
@ -5712,7 +5710,7 @@
"gl": "Lingua khmer",
"he": "קמרית",
"hu": "khmer",
"id": "Khmer",
"id": "bahasa Khmer",
"it": "khmer",
"ja": "クメール語",
"nb_NO": "khmer",
@ -5823,7 +5821,6 @@
"pl": "język komi-permiacki",
"pt": "Língua komi-permyak",
"ru": "коми-пермяцкий язык",
"sl": "permjaščina",
"sv": "komi-permjakiska",
"zh_Hans": "彼尔姆科米语",
"zh_Hant": "彼爾姆科米語",
@ -6033,32 +6030,32 @@
}
},
"ku": {
"ca": "kurd",
"cs": "kurdština",
"da": "kurdisk",
"de": "Kurdisch",
"en": "Kurdish",
"eo": "kurda lingvo",
"es": "kurdo",
"eu": "kurduera",
"fi": "kurdi",
"fr": "kurde",
"ca": "kurd del nord",
"cs": "kurmándží",
"da": "Kurmanji",
"de": "Kurmandschi",
"en": "Kurmanji",
"eo": "kurmanĝa lingvo",
"es": "kurmanji",
"eu": "Kurmanji",
"fi": "Kurmandži",
"fr": "kurmandji",
"gl": "lingua kurda",
"he": "כורדית",
"hu": "kurd",
"id": "Bahasa Kurdi",
"it": "curdo",
"ja": "クルド語",
"he": "כורמנג'ית",
"hu": "kurmandzsi",
"id": "Kurmanji",
"it": "kurmanji",
"ja": "クルマンジー",
"nb_NO": "kurdisk",
"nl": "Koerdisch",
"pl": "język kurdyjski",
"pt": "língua curda",
"pt_BR": "língua curda",
"ru": "курдские языки",
"sl": "kurdščina",
"sv": "kurdiska",
"nl": "Kurmançi",
"pl": "język kurmandżi",
"pt": "curmânji",
"pt_BR": "Curmânji",
"ru": "курманджи",
"sl": "kurmandži",
"sv": "nordkurdiska",
"zh_Hans": "库尔德语",
"zh_Hant": "庫德語",
"zh_Hant": "庫德語",
"_meta": {
"countries": [
"IQ"
@ -6135,7 +6132,7 @@
"gl": "lingua komi",
"he": "קומי",
"hu": "komi",
"id": "Komi",
"id": "Bahasa Komi",
"it": "comi",
"ja": "コミ語",
"nb_NO": "syrjensk",
@ -6143,7 +6140,6 @@
"pl": "język komi",
"pt": "língua komi",
"ru": "коми язык",
"sl": "komijščina",
"sv": "komi",
"_meta": {
"dir": [
@ -6226,7 +6222,7 @@
"gl": "kirguiz",
"he": "קירגיזית",
"hu": "kirgiz",
"id": "Kirgiz",
"id": "bahasa Kirgiz",
"it": "kirghiso",
"ja": "キルギス語",
"nb_NO": "kirgisisk",
@ -6312,7 +6308,7 @@
"gl": "Lingua luxemburguesa",
"he": "לוקסמבורגית",
"hu": "luxemburgi",
"id": "Luksemburg",
"id": "bahasa Luksemburg",
"it": "lussemburghese",
"ja": "ルクセンブルク語",
"nb_NO": "luxembourgsk",
@ -6552,7 +6548,7 @@
"gl": "Lingua lombarda",
"he": "לומברד (שפה)",
"hu": "lombard",
"id": "Lombard",
"id": "bahasa Lombard",
"it": "lingua lombarda",
"ja": "ロンバルド語",
"nb_NO": "lombardisk",
@ -6612,7 +6608,7 @@
"gl": "Lingua laosiana",
"he": "לאית",
"hu": "lao",
"id": "Lao",
"id": "bahasa Lao",
"it": "lao",
"ja": "ラーオ語",
"nb_NO": "laotisk",
@ -6982,7 +6978,7 @@
"gl": "Lingua malgaxe",
"he": "מלגשית",
"hu": "malgas",
"id": "Malagasi",
"id": "Bahasa Malagasi",
"it": "malgascio",
"ja": "マダガスカル語",
"nb_NO": "gassisk",
@ -7168,7 +7164,7 @@
"gl": "Lingua macedonia",
"he": "מקדונית",
"hu": "macedón",
"id": "Makedonia",
"id": "bahasa Makedonia",
"it": "macedone",
"ja": "マケドニア語",
"nb_NO": "makedonsk",
@ -7236,7 +7232,7 @@
"gl": "Lingua mongol",
"he": "מונגולית",
"hu": "mongol",
"id": "Mongol",
"id": "bahasa Mongol",
"it": "mongolo",
"ja": "モンゴル語",
"nb_NO": "mongolsk",
@ -7477,7 +7473,7 @@
"gl": "lingua malaia",
"he": "מלאית",
"hu": "maláj",
"id": "Melayu",
"id": "bahasa Melayu",
"it": "malese",
"ja": "マレー語",
"nb_NO": "malayisk",
@ -7655,7 +7651,7 @@
"gl": "birmano",
"he": "בורמזית",
"hu": "burmai",
"id": "Burma",
"id": "bahasa Burma",
"it": "birmano",
"ja": "ビルマ語",
"nb_NO": "burmesisk",
@ -8117,7 +8113,7 @@
"gl": "lingua norueguesa",
"he": "נורווגית",
"hu": "norvég",
"id": "Norwegia",
"id": "bahasa Norwegia",
"it": "norvegese",
"ja": "ノルウェー語",
"nb_NO": "norsk",
@ -8444,12 +8440,12 @@
"eo": "olonec-karela lingvo",
"fi": "livvinkarjala",
"fr": "olonetsien",
"gl": "lingua livvi",
"gl": "Lingua livvi",
"it": "lingua livvi",
"ja": "リッヴィ語",
"nb_NO": "livvisk",
"nl": "Olonetsisch",
"pl": "dialekt ołoniecki",
"pl": "Dialekt ołoniecki",
"ru": "ливвиковское наречие",
"sv": "livvi",
"zh_Hant": "利維卡累利阿語",
@ -8554,7 +8550,7 @@
"gl": "Lingua oseta",
"he": "אוסטית",
"hu": "oszét",
"id": "Ossetia",
"id": "bahasa Ossetia",
"it": "osseto",
"ja": "オセット語",
"nb_NO": "ossetisk",
@ -8630,7 +8626,7 @@
"gl": "lingua punjabi (Shahmukhi)",
"he": "פנג'אבי (אלפבית שאהמוקי)",
"hu": "pandzsábi (Shahmukhi)",
"id": "Punjab (Abjad Shahmukhi)",
"id": "Bahasa Punjab (Abjad Shahmukhi)",
"it": "punjabi (Shahmukhī)",
"ja": "パンジャーブ語 (シャームキー文字)",
"nb_NO": "panjabi (Shahmukhi)",
@ -8855,7 +8851,6 @@
"pl": "Język neosalomoński",
"pt": "Língua pijin",
"ru": "Пиджин Соломоновых Островов",
"sl": "salomonski pidžin",
"sv": "pijin",
"_meta": {
"dir": [
@ -9053,7 +9048,7 @@
"gl": "lingua portuguesa",
"he": "פורטוגזית",
"hu": "portugál",
"id": "Portugis",
"id": "bahasa Portugis",
"it": "portoghese",
"ja": "ポルトガル語",
"nb_NO": "portugisisk",
@ -9263,7 +9258,7 @@
"en": "Rakhine",
"fr": "arakanais",
"gl": "Lingua arakanesa",
"id": "Rakhine",
"id": "bahasa Rakhine",
"ja": "ラカイン語",
"nl": "Arakanees",
"pl": "Język arakański",
@ -9511,7 +9506,7 @@
"gl": "Lingua arromanesa",
"he": "ארומנית",
"hu": "aromán",
"id": "Arumania",
"id": "Bahasa Arumania",
"it": "arumeno",
"ja": "アルーマニア語",
"nb_NO": "arumensk",
@ -9906,7 +9901,7 @@
"ca": "taixelhit",
"cs": "tašelhit",
"de": "Taschelhit",
"en": "Tachelhit",
"en": "Shilha",
"eo": "ŝelha lingvo",
"es": "chilha",
"fi": "Tašelhit",
@ -10006,7 +10001,7 @@
"pt": "Língua cingalesa",
"pt_BR": "Língua cingalesa",
"ru": "сингальский язык",
"sl": "singalščina",
"sl": "sinhalščina",
"sv": "singalesiska",
"zh_Hant": "僧伽羅語",
"_meta": {
@ -10466,7 +10461,7 @@
"gl": "Lingua albanesa",
"he": "אלבנית",
"hu": "albán",
"id": "Albania",
"id": "Bahasa Albania",
"it": "albanese",
"ja": "アルバニア語",
"nb_NO": "albansk",
@ -10709,7 +10704,7 @@
"gl": "lingua sueca",
"he": "שוודית",
"hu": "svéd",
"id": "Swedia",
"id": "bahasa Swedia",
"it": "svedese",
"ja": "スウェーデン語",
"nb_NO": "svensk",
@ -10808,7 +10803,7 @@
"gl": "Lingua silesiana",
"he": "שלזית",
"hu": "sziléziai",
"id": "Silesia",
"id": "bahasa Silesia",
"it": "slesiano",
"ja": "シレジア語",
"nb_NO": "schlesisk",
@ -10857,7 +10852,7 @@
"gl": "Lingua támil",
"he": "טמילית",
"hu": "tamil",
"id": "Tamil",
"id": "Bahasa Tamil",
"it": "tamil",
"ja": "タミル語",
"nb_NO": "tamilsk",
@ -11044,7 +11039,7 @@
"gl": "lingua tailandesa",
"he": "תאית",
"hu": "thai",
"id": "Thai",
"id": "bahasa Thai",
"it": "thailandese",
"ja": "タイ語",
"nb_NO": "thai",
@ -11114,7 +11109,7 @@
"gl": "Lingua turcomá",
"he": "טורקמנית",
"hu": "türkmén",
"id": "Turkmen",
"id": "bahasa Turkmen",
"it": "Turkmeno",
"ja": "トルクメン語",
"nb_NO": "turkmensk",
@ -11642,7 +11637,7 @@
"gl": "Lingua uigur",
"he": "אויגורית",
"hu": "ujgur",
"id": "Uighur",
"id": "bahasa Uyghur",
"it": "uiguro",
"ja": "ウイグル語",
"nb_NO": "uigurisk",
@ -11712,7 +11707,7 @@
"gl": "Lingua usbeka",
"he": "אוזבקית",
"hu": "üzbég",
"id": "Uzbek",
"id": "bahasa Uzbek",
"it": "uzbeco",
"ja": "ウズベク語",
"nb_NO": "usbekisk",
@ -12601,7 +12596,7 @@
"gl": "lingua chinesa",
"he": "סינית",
"hu": "kínai",
"id": "Tionghoa",
"id": "bahasa Tionghoa",
"it": "cinese",
"ja": "中国語",
"nb_NO": "kinesisk",
@ -12657,7 +12652,7 @@
]
}
},
"zh_Hant": {
"zh_Hans": {
"ca": "xinès simplificat",
"cs": "zjednodušená čínština",
"da": "forenklet kinesisk",
@ -12666,7 +12661,6 @@
"eo": "simpligita ĉina skribsistemo",
"es": "chino simplificado",
"eu": "Txinera sinplifikatua",
"fi": "perinteinen kiina",
"fr": "chinois simplifié",
"gl": "chinés simplificado",
"he": "סינית מפושטת",
@ -12689,6 +12683,36 @@
]
}
},
"zh_Hant": {
"ca": "xinès tradicional",
"cs": "čínština (tradiční)",
"da": "traditionel kinesisk",
"de": "traditionelles Chinesisch",
"en": "Traditional Chinese",
"eo": "ĉina lingvo de tradicia ortografio",
"es": "chino tradicional",
"eu": "Txinera tradizional",
"fi": "perinteinen kiina",
"fr": "chinois traditionnel",
"gl": "chinés tradicional",
"he": "סינית מסורתית",
"it": "cinese tradizionale",
"ja": "繁体字中国語",
"nb_NO": "tradisjonell kinesisk",
"nl": "traditioneel Chinees",
"pl": "język chiński tradycyjny",
"pt": "chinês tradicional",
"ru": "традиционный китайский",
"sl": "tradicionalna kitajščina",
"sv": "traditionell kinesiska",
"zh_Hans": "繁体中文",
"zh_Hant": "繁體中文",
"_meta": {
"dir": [
"left-to-right"
]
}
},
"zu": {
"ca": "zulu",
"cs": "zuluština",

View file

@ -296,6 +296,10 @@
"commits": 4,
"contributor": "Jan Zabel"
},
{
"commits": 3,
"contributor": "Peter Brodersen"
},
{
"commits": 3,
"contributor": "ssantos"
@ -360,6 +364,10 @@
"commits": 3,
"contributor": "SiegbjornSitumeang"
},
{
"commits": 2,
"contributor": "Smith Brown"
},
{
"commits": 2,
"contributor": "Michel"
@ -368,10 +376,6 @@
"commits": 2,
"contributor": "Kelson Vibber"
},
{
"commits": 2,
"contributor": "Peter Brodersen"
},
{
"commits": 2,
"contributor": "nilocram"