forked from MapComplete/MapComplete
Chore: Housekeeping
This commit is contained in:
parent
ef0ba091eb
commit
319c0e2573
77 changed files with 2485 additions and 1727 deletions
|
@ -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
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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(/</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<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(/</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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()}
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
{/each}
|
||||
</div>
|
||||
|
||||
|
||||
{#if filteredThemes.length === 0}
|
||||
<NoThemeResultButton {search} />
|
||||
{/if}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
let gotMeasurement = o.gotMeasurement
|
||||
o.startMeasurements()
|
||||
</script>
|
||||
|
||||
{#if !$gotMeasurement}
|
||||
No device orientation data available
|
||||
{:else}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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": "繁體中文"
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue