Merge branches

This commit is contained in:
Pieter Vander Vennet 2024-05-13 18:55:54 +02:00
commit bae90d50bc
304 changed files with 49983 additions and 31589 deletions

View file

@ -16,6 +16,7 @@ export class LastClickFeatureSource {
private i: number = 0
private readonly hasPresets: boolean
private readonly hasNoteLayer: boolean
public static readonly newPointElementId= "new_point_dialog"
constructor(layout: LayoutConfig) {
this.hasNoteLayer = layout.hasNoteLayer()
@ -46,7 +47,7 @@ export class LastClickFeatureSource {
public createFeature(lon: number, lat: number): Feature<Point, OsmTags> {
const properties: OsmTags = {
id: "new_point_dialog",
id: LastClickFeatureSource.newPointElementId,
has_note_layer: this.hasNoteLayer ? "yes" : "no",
has_presets: this.hasPresets ? "yes" : "no",
renderings: this.renderings.join(""),

View file

@ -1,5 +1,9 @@
import Constants from "../Models/Constants"
export interface MaprouletteTask {
name: string,
description: string,
instruction: string
}
export default class Maproulette {
public static readonly defaultEndpoint = "https://maproulette.org/api/v2"

View file

@ -19,6 +19,7 @@ import Title from "../../UI/Base/Title"
import Table from "../../UI/Base/Table"
import ChangeLocationAction from "./Actions/ChangeLocationAction"
import ChangeTagAction from "./Actions/ChangeTagAction"
import FeatureSwitchState from "../State/FeatureSwitchState"
/**
* Handles all changes made to OSM.
@ -28,7 +29,7 @@ export class Changes {
public readonly pendingChanges: UIEventSource<ChangeDescription[]> =
LocalStorageSource.GetParsed<ChangeDescription[]>("pending-changes", [])
public readonly allChanges = new UIEventSource<ChangeDescription[]>(undefined)
public readonly state: { allElements?: IndexedFeatureSource; osmConnection: OsmConnection }
public readonly state: { allElements?: IndexedFeatureSource; osmConnection: OsmConnection, featureSwitches?: FeatureSwitchState }
public readonly extraComment: UIEventSource<string> = new UIEventSource(undefined)
public readonly backend: string
public readonly isUploading = new UIEventSource(false)
@ -45,7 +46,8 @@ export class Changes {
allElements?: IndexedFeatureSource
featurePropertiesStore?: FeaturePropertiesStore
osmConnection: OsmConnection
historicalUserLocations?: FeatureSource
historicalUserLocations?: FeatureSource,
featureSwitches?: FeatureSwitchState
},
leftRightSensitive: boolean = false
) {
@ -431,6 +433,9 @@ export class Changes {
// Probably irrelevant, such as a new helper node
return
}
if(this.state.featureSwitches.featureSwitchMorePrivacy?.data){
return
}
const now = new Date()
const recentLocationPoints = locations

View file

@ -6,14 +6,6 @@ import Constants from "../../Models/Constants"
import { Changes } from "./Changes"
import { Utils } from "../../Utils"
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
import ChangeLocationAction from "./Actions/ChangeLocationAction"
import ChangeTagAction from "./Actions/ChangeTagAction"
import DeleteAction from "./Actions/DeleteAction"
import LinkImageAction from "./Actions/LinkImageAction"
import OsmChangeAction from "./Actions/OsmChangeAction"
import RelationSplitHandler from "./Actions/RelationSplitHandler"
import ReplaceGeometryAction from "./Actions/ReplaceGeometryAction"
import SplitAction from "./Actions/SplitAction"
export interface ChangesetTag {
key: string
@ -232,7 +224,7 @@ export class ChangesetHandler {
if (newMetaTag === undefined) {
extraMetaTags.push({
key: key,
value: oldCsTags[key],
value: oldCsTags[key]
})
continue
}
@ -361,21 +353,22 @@ export class ChangesetHandler {
}
private defaultChangesetTags(): ChangesetTag[] {
const usedGps = this.changes.state["currentUserLocation"]?.features?.data?.length > 0
const hasMorePrivacy = !!this.changes.state?.featureSwitches?.featureSwitchMorePrivacy?.data
const setSourceAsSurvey = !hasMorePrivacy && usedGps
return [
["created_by", `MapComplete ${Constants.vNumber}`],
["locale", Locale.language.data],
["host", `${window.location.origin}${window.location.pathname}`],
[
"source",
this.changes.state["currentUserLocation"]?.features?.data?.length > 0
? "survey"
: undefined,
setSourceAsSurvey ? "survey" : undefined
],
["imagery", this.changes.state["backgroundLayer"]?.data?.id],
["imagery", this.changes.state["backgroundLayer"]?.data?.id]
].map(([key, value]) => ({
key,
value,
aggregate: false,
aggregate: false
}))
}

View file

@ -1,4 +1,3 @@
// @ts-ignore
import { osmAuth } from "osm-auth"
import { Store, Stores, UIEventSource } from "../UIEventSource"
import { OsmPreferences } from "./OsmPreferences"
@ -6,7 +5,18 @@ import { Utils } from "../../Utils"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { AuthConfig } from "./AuthConfig"
import Constants from "../../Models/Constants"
import OSMAuthInstance = OSMAuth.OSMAuthInstance
interface OsmUserInfo {
id: number
display_name: string
account_created: string
description: string
contributor_terms: { agreed: boolean }
roles: []
changesets: { count: number }
traces: { count: number }
blocks: { received: { count: number; active: number } }
}
export default class UserDetails {
public loggedIn = false
@ -31,7 +41,7 @@ export default class UserDetails {
export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable"
export class OsmConnection {
public auth: OSMAuthInstance
public auth: osmAuth
public userDetails: UIEventSource<UserDetails>
public isLoggedIn: Store<boolean>
public gpxServiceIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>(
@ -49,7 +59,7 @@ export class OsmConnection {
private readonly _dryRun: Store<boolean>
private readonly fakeUser: boolean
private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []
private readonly _iframeMode: Boolean | boolean
private readonly _iframeMode: boolean
private readonly _singlePage: boolean
private isChecking = false
private readonly _doCheckRegularly
@ -99,20 +109,19 @@ export class OsmConnection {
ud.languages = ["en"]
this.loadingStatus.setData("logged-in")
}
const self = this
this.UpdateCapabilities()
this.isLoggedIn = this.userDetails.map(
(user) =>
user.loggedIn &&
(self.apiIsOnline.data === "unknown" || self.apiIsOnline.data === "online"),
(this.apiIsOnline.data === "unknown" || this.apiIsOnline.data === "online"),
[this.apiIsOnline]
)
this.isLoggedIn.addCallback((isLoggedIn) => {
if (self.userDetails.data.loggedIn == false && isLoggedIn == true) {
if (this.userDetails.data.loggedIn == false && isLoggedIn == true) {
// We have an inconsistency: the userdetails say we _didn't_ log in, but this actor says we do
// This means someone attempted to toggle this; so we attempt to login!
self.AttemptLogin()
this.AttemptLogin()
}
})
@ -120,37 +129,36 @@ export class OsmConnection {
this.updateAuthObject()
if (!this.fakeUser) {
self.CheckForMessagesContinuously()
this.CheckForMessagesContinuously()
}
this.preferencesHandler = new OsmPreferences(this.auth, this, this.fakeUser)
if (options.oauth_token?.data !== undefined) {
console.log(options.oauth_token.data)
const self = this
this.auth.bootstrapToken(options.oauth_token.data, (err, result) => {
console.log("Bootstrap token called back", err, result)
self.AttemptLogin()
this.AttemptLogin()
})
options.oauth_token.setData(undefined)
}
if (this.auth.authenticated() && options.attemptLogin !== false) {
if (!Utils.runningFromConsole && this.auth.authenticated() && options.attemptLogin !== false) {
this.AttemptLogin()
} else {
console.log("Not authenticated")
}
}
public GetPreference(
public GetPreference<T extends string = string>(
key: string,
defaultValue: string = undefined,
options?: {
documentation?: string
prefix?: string
}
): UIEventSource<string> {
return this.preferencesHandler.GetPreference(key, defaultValue, options)
): UIEventSource<T | undefined> {
return <UIEventSource<T>>this.preferencesHandler.GetPreference(key, defaultValue, options)
}
public GetLongPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
@ -192,7 +200,7 @@ export class OsmConnection {
console.log("AttemptLogin called, but ignored as fakeUser is set")
return
}
const self = this
console.log("Trying to log in...")
this.updateAuthObject()
@ -202,33 +210,33 @@ export class OsmConnection {
this.auth.xhr(
{
method: "GET",
path: "/api/0.6/user/details",
path: "/api/0.6/user/details"
},
function (err, details: XMLDocument) {
(err, details: XMLDocument) => {
if (err != null) {
console.log("Could not login due to:", err)
self.loadingStatus.setData("error")
this.loadingStatus.setData("error")
if (err.status == 401) {
console.log("Clearing tokens...")
// Not authorized - our token probably got revoked
self.auth.logout()
self.LogOut()
this.auth.logout()
this.LogOut()
} else {
console.log("Other error. Status:", err.status)
self.apiIsOnline.setData("unreachable")
this.apiIsOnline.setData("unreachable")
}
return
}
if (details == null) {
self.loadingStatus.setData("error")
this.loadingStatus.setData("error")
return
}
// details is an XML DOM of user details
let userInfo = details.getElementsByTagName("user")[0]
const userInfo = details.getElementsByTagName("user")[0]
let data = self.userDetails.data
const data = this.userDetails.data
data.loggedIn = true
console.log("Login completed, userinfo is ", userInfo)
data.name = userInfo.getAttribute("display_name")
@ -261,18 +269,18 @@ export class OsmConnection {
data.home = { lat: lat, lon: lon }
}
self.loadingStatus.setData("logged-in")
this.loadingStatus.setData("logged-in")
const messages = userInfo
.getElementsByTagName("messages")[0]
.getElementsByTagName("received")[0]
data.unreadMessages = parseInt(messages.getAttribute("unread"))
data.totalMessages = parseInt(messages.getAttribute("count"))
self.userDetails.ping()
for (const action of self._onLoggedIn) {
action(self.userDetails.data)
this.userDetails.ping()
for (const action of this._onLoggedIn) {
action(this.userDetails.data)
}
self._onLoggedIn = []
this._onLoggedIn = []
}
)
}
@ -289,11 +297,11 @@ export class OsmConnection {
public async interact(
path: string,
method: "GET" | "POST" | "PUT" | "DELETE",
header?: Record<string, string | number>,
header?: Record<string, string>,
content?: string,
allowAnonymous: boolean = false
): Promise<string> {
let connection: OSMAuthInstance = this.auth
const connection: osmAuth = this.auth
if (allowAnonymous && !this.auth.authenticated()) {
const possibleResult = await Utils.downloadAdvanced(
`${this.Backend()}/api/0.6/${path}`,
@ -310,15 +318,13 @@ export class OsmConnection {
return new Promise((ok, error) => {
connection.xhr(
<any>{
{
method,
options: {
header,
},
headers: header,
content,
path: `/api/0.6/${path}`,
path: `/api/0.6/${path}`
},
function (err, response) {
function(err, response) {
if (err !== null) {
error(err)
} else {
@ -329,32 +335,32 @@ export class OsmConnection {
})
}
public async post(
public async post<T extends string>(
path: string,
content?: string,
header?: Record<string, string | number>,
header?: Record<string, string>,
allowAnonymous: boolean = false
): Promise<any> {
return await this.interact(path, "POST", header, content, allowAnonymous)
): Promise<T> {
return <T> await this.interact(path, "POST", header, content, allowAnonymous)
}
public async put(
public async put<T extends string>(
path: string,
content?: string,
header?: Record<string, string | number>
): Promise<any> {
return await this.interact(path, "PUT", header, content)
header?: Record<string, string>
): Promise<T> {
return <T> await this.interact(path, "PUT", header, content)
}
public async get(
path: string,
header?: Record<string, string | number>,
header?: Record<string, string>,
allowAnonymous: boolean = false
): Promise<string> {
return await this.interact(path, "GET", header, undefined, allowAnonymous)
}
public closeNote(id: number | string, text?: string): Promise<void> {
public closeNote(id: number | string, text?: string): Promise<string> {
let textSuffix = ""
if ((text ?? "") !== "") {
textSuffix = "?text=" + encodeURIComponent(text)
@ -362,17 +368,17 @@ export class OsmConnection {
if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually closing note ", id, " with text ", text)
return new Promise((ok) => {
ok()
ok("")
})
}
return this.post(`notes/${id}/close${textSuffix}`)
}
public reopenNote(id: number | string, text?: string): Promise<void> {
public reopenNote(id: number | string, text?: string): Promise<string> {
if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually reopening note ", id, " with text ", text)
return new Promise((ok) => {
ok()
return new Promise(resolve => {
resolve("")
})
}
let textSuffix = ""
@ -398,7 +404,7 @@ export class OsmConnection {
"notes.json",
content,
{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
true
)
@ -439,7 +445,7 @@ export class OsmConnection {
file: gpx,
description: options.description,
tags: options.labels?.join(",") ?? "",
visibility: options.visibility,
visibility: options.visibility
}
if (!contents.description) {
@ -447,9 +453,9 @@ export class OsmConnection {
}
const extras = {
file:
'; filename="' +
"; filename=\"" +
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
'"\r\nContent-Type: application/gpx+xml',
"\"\r\nContent-Type: application/gpx+xml"
}
const boundary = "987654"
@ -457,7 +463,7 @@ export class OsmConnection {
let body = ""
for (const key in contents) {
body += "--" + boundary + "\r\n"
body += 'Content-Disposition: form-data; name="' + key + '"'
body += "Content-Disposition: form-data; name=\"" + key + "\""
if (extras[key] !== undefined) {
body += extras[key]
}
@ -468,7 +474,7 @@ export class OsmConnection {
const response = await this.post("gpx/create", body, {
"Content-Type": "multipart/form-data; boundary=" + boundary,
"Content-Length": body.length,
"Content-Length": ""+body.length
})
const parsed = JSON.parse(response)
console.log("Uploaded GPX track", parsed)
@ -491,9 +497,9 @@ export class OsmConnection {
{
method: "POST",
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`
},
function (err, _) {
function(err) {
if (err !== null) {
error(err)
} else {
@ -508,7 +514,7 @@ export class OsmConnection {
* To be called by land.html
*/
public finishLogin(callback: (previousURL: string) => void) {
this.auth.authenticate(function () {
this.auth.authenticate(function() {
// Fully authed at this point
console.log("Authentication successful!")
const previousLocation = LocalStorageSource.Get("location_before_login")
@ -517,28 +523,6 @@ export class OsmConnection {
}
private updateAuthObject() {
let pwaStandAloneMode = false
try {
if (Utils.runningFromConsole) {
pwaStandAloneMode = true
} else if (
window.matchMedia("(display-mode: standalone)").matches ||
window.matchMedia("(display-mode: fullscreen)").matches
) {
pwaStandAloneMode = true
}
} catch (e) {
console.warn(
"Detecting standalone mode failed",
e,
". Assuming in browser and not worrying furhter"
)
}
const standalone = this._iframeMode || pwaStandAloneMode || !this._singlePage
// In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway...
// Same for an iframe...
this.auth = new osmAuth({
client_id: this._oauth_config.oauth_client_id,
url: this._oauth_config.url,
@ -546,23 +530,22 @@ export class OsmConnection {
redirect_uri: Utils.runningFromConsole
? "https://mapcomplete.org/land.html"
: window.location.protocol + "//" + window.location.host + "/land.html",
singlepage: true,
auto: true,
singlepage: true, // We always use 'singlePage', it is the most stable - including in PWA
auto: true
})
}
private CheckForMessagesContinuously() {
const self = this
if (this.isChecking) {
return
}
Stores.Chronic(3 * 1000).addCallback((_) => {
if (!(self.apiIsOnline.data === "unreachable" || self.apiIsOnline.data === "offline")) {
Stores.Chronic(3 * 1000).addCallback(() => {
if (!(this.apiIsOnline.data === "unreachable" || this.apiIsOnline.data === "offline")) {
return
}
try {
console.log("Api is offline - trying to reconnect...")
self.AttemptLogin()
this.AttemptLogin()
} catch (e) {
console.log("Could not login due to", e)
}
@ -571,10 +554,10 @@ export class OsmConnection {
if (!this._doCheckRegularly) {
return
}
Stores.Chronic(60 * 5 * 1000).addCallback((_) => {
if (self.isLoggedIn.data) {
Stores.Chronic(60 * 5 * 1000).addCallback(() => {
if (this.isLoggedIn.data) {
try {
self.AttemptLogin()
this.AttemptLogin()
} catch (e) {
console.log("Could not login due to", e)
}
@ -592,19 +575,9 @@ export class OsmConnection {
})
}
private readonly _userInfoCache: Record<number, any> = {}
private readonly _userInfoCache: Record<number, OsmUserInfo> = {}
public async getInformationAboutUser(id: number): Promise<{
id: number
display_name: string
account_created: string
description: string
contributor_terms: { agreed: boolean }
roles: []
changesets: { count: number }
traces: { count: number }
blocks: { received: { count: number; active: number } }
}> {
public async getInformationAboutUser(id: number): Promise<OsmUserInfo> {
if (id === undefined) {
return undefined
}

View file

@ -63,6 +63,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
public readonly overpassMaxZoom: UIEventSource<number>
public readonly osmApiTileSize: UIEventSource<number>
public readonly backgroundLayerId: UIEventSource<string>
public readonly featureSwitchMorePrivacy: UIEventSource<boolean>
public constructor(layoutToUse?: LayoutConfig) {
super()
@ -164,6 +165,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
"If true, shows some extra debugging help such as all the available tags on every object"
)
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
"moreprivacy",
layoutToUse.enableMorePrivacy,
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken."
)
this.overpassUrl = QueryParameters.GetQueryParameter(
"overpassUrl",
(layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),

View file

@ -43,6 +43,7 @@ export default class UserRelatedState {
public readonly fixateNorth: UIEventSource<undefined | "yes">
public readonly a11y: UIEventSource<undefined | "always" | "never" | "default">
public readonly homeLocation: FeatureSource
public readonly morePrivacy: UIEventSource<undefined | "yes" | "no">
/**
* The language as saved into the preferences of the user, if logged in.
* Note that this is _different_ from the languages a user can set via the osm.org interface here: https://www.openstreetmap.org/preferences
@ -106,12 +107,12 @@ export default class UserRelatedState {
})
)
this.language = this.osmConnection.GetPreference("language")
this.showTags = <UIEventSource<any>>this.osmConnection.GetPreference("show_tags")
this.showCrosshair = <UIEventSource<any>>this.osmConnection.GetPreference("show_crosshair")
this.fixateNorth = <UIEventSource<"yes">>this.osmConnection.GetPreference("fixate-north")
this.a11y = <UIEventSource<"always" | "never" | "default">>(
this.osmConnection.GetPreference("a11y")
)
this.showTags = this.osmConnection.GetPreference("show_tags")
this.showCrosshair = this.osmConnection.GetPreference("show_crosshair")
this.fixateNorth = this.osmConnection.GetPreference("fixate-north")
this.morePrivacy = this.osmConnection.GetPreference("more_privacy", "no")
this.a11y = this.osmConnection.GetPreference("a11y")
this.mangroveIdentity = new MangroveIdentity(
this.osmConnection.GetLongPreference("identity", "mangrove"),

View file

@ -5,6 +5,7 @@ import { Tag } from "./Tag"
import { RegexTag } from "./RegexTag"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
import { ExpressionSpecification } from "maplibre-gl"
import ComparingTag from "./ComparingTag"
export class And extends TagsFilter {
public and: TagsFilter[]
@ -242,6 +243,27 @@ export class And extends TagsFilter {
* const raw = {"and": [{"and":["advertising=screen"]}, {"and":["advertising~*"]}]}]
* const parsed = TagUtils.Tag(raw)
* parsed.optimize().asJson() // => "advertising=screen"
*
* const raw = {"and": ["count=0", "count>0"]}
* const parsed = TagUtils.Tag(raw)
* parsed.optimize() // => false
*
* const raw = {"and": ["count>0", "count>10"]}
* const parsed = TagUtils.Tag(raw)
* parsed.optimize().asJson() // => "count>0"
*
* // regression test
* const orig = {
* "and": [
* "sport=climbing",
* "climbing!~route",
* "climbing!=route_top",
* "climbing!=route_bottom",
* "leisure!~sports_centre"
* ]
* }
* const parsed = TagUtils.Tag(orig)
* parsed.optimize().asJson() // => orig
*/
optimize(): TagsFilter | boolean {
if (this.and.length === 0) {
@ -256,9 +278,30 @@ export class And extends TagsFilter {
}
const optimized = <TagsFilter[]>optimizedRaw
for (let i = 0; i <optimized.length; i++) {
for (let j = i + 1; j < optimized.length; j++) {
const ti = optimized[i]
const tj = optimized[j]
if(ti.shadows(tj)){
// if 'ti' is true, this implies 'tj' is always true as well.
// if 'ti' is false, then 'tj' might be true or false
// (e.g. let 'ti' be 'count>0' and 'tj' be 'count>10'.
// As such, it is no use to keep 'tj' around:
// If 'ti' is true, then 'tj' will be true too and 'tj' can be ignored
// If 'ti' is false, then the entire expression will be false and it doesn't matter what 'tj' yields
optimized.splice(j, 1)
}else if (tj.shadows(ti)){
optimized.splice(i, 1)
i--
continue
}
}
}
{
// Conflicting keys do return false
const properties: object = {}
const properties: Record<string, string> = {}
for (const opt of optimized) {
if (opt instanceof Tag) {
properties[opt.key] = opt.value
@ -277,8 +320,7 @@ export class And extends TagsFilter {
// detected an internal conflict
return false
}
}
if (opt instanceof RegexTag) {
} else if (opt instanceof RegexTag) {
const k = opt.key
if (typeof k !== "string") {
continue
@ -316,6 +358,11 @@ export class And extends TagsFilter {
i--
}
}
}else if(opt instanceof ComparingTag) {
const ct = opt
if(properties[ct.key] !== undefined && !ct.matchesProperties(properties)){
return false
}
}
}
}

View file

@ -1,10 +1,9 @@
import { TagsFilter } from "./TagsFilter"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
import { Tag } from "./Tag"
import { ExpressionSpecification } from "maplibre-gl"
export default class ComparingTag extends TagsFilter {
private readonly _key: string
public readonly key: string
private readonly _predicate: (value: string) => boolean
private readonly _representation: "<" | ">" | "<=" | ">="
private readonly _boundary: string
@ -16,7 +15,7 @@ export default class ComparingTag extends TagsFilter {
boundary: string
) {
super()
this._key = key
this.key = key
this._predicate = predicate
this._representation = representation
this._boundary = boundary
@ -27,7 +26,7 @@ export default class ComparingTag extends TagsFilter {
}
asHumanString() {
return this._key + this._representation + this._boundary
return this.asJson()
}
asOverpass(): string[] {
@ -55,7 +54,7 @@ export default class ComparingTag extends TagsFilter {
return true
}
if (other instanceof ComparingTag) {
if (other._key !== this._key) {
if (other.key !== this.key) {
return false
}
const selfDesc = this._representation === "<" || this._representation === "<="
@ -76,7 +75,7 @@ export default class ComparingTag extends TagsFilter {
}
if (other instanceof Tag) {
if (other.key !== this._key) {
if (other.key !== this.key) {
return false
}
if (this.matchesProperties({ [other.key]: other.value })) {
@ -101,19 +100,25 @@ export default class ComparingTag extends TagsFilter {
* t.matchesProperties({differentKey: 42}) // => false
*/
matchesProperties(properties: Record<string, string>): boolean {
return this._predicate(properties[this._key])
return this._predicate(properties[this.key])
}
usedKeys(): string[] {
return [this._key]
return [this.key]
}
usedTags(): { key: string; value: string }[] {
return []
}
asJson(): TagConfigJson {
return this._key + this._representation
/**
* import { TagUtils } from "../../../src/Logic/Tags/TagUtils"
*
* TagUtils.Tag("count>42").asJson() // => "count>42"
* TagUtils.Tag("count<0").asJson() // => "count<0"
*/
asJson(): string {
return this.key + this._representation + this._boundary
}
optimize(): TagsFilter | boolean {
@ -124,11 +129,11 @@ export default class ComparingTag extends TagsFilter {
return true
}
visit(f: (TagsFilter) => void) {
visit(f: (tf: TagsFilter) => void) {
f(this)
}
asMapboxExpression(): ExpressionSpecification {
return [this._representation, ["get", this._key], this._boundary]
return [this._representation, ["get", this.key], this._boundary]
}
}

View file

@ -259,6 +259,13 @@ export class RegexTag extends TagsFilter {
* new RegexTag("key",/^..*$/, true).shadows(new Tag("key","")) // => true
* new RegexTag("key","value", true).shadows(new Tag("key","value")) // => false
* new RegexTag("key","value", true).shadows(new Tag("key","some_other_value")) // => false
* new RegexTag("key","value", true).shadows(new Tag("key","some_other_value", true)) // => false
*
* const route = TagUtils.Tag("climbing!~route")
* const routeBottom = TagUtils.Tag("climbing!~route_bottom")
* route.shadows(routeBottom) // => false
* routeBottom.shadows(route) // => false
*
*/
shadows(other: TagsFilter): boolean {
if (other instanceof RegexTag) {
@ -267,7 +274,7 @@ export class RegexTag extends TagsFilter {
return false
}
if (
(other.value["source"] ?? other.key) === (this.value["source"] ?? this.key) &&
(other.value["source"] ?? other.value) === (this.value["source"] ?? this.value) &&
this.invert == other.invert
) {
// Values (and inverts) match

View file

@ -2,6 +2,7 @@ import { Utils } from "../../Utils"
import { TagsFilter } from "./TagsFilter"
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
import { ExpressionSpecification } from "maplibre-gl"
import { RegexTag } from "./RegexTag"
export class Tag extends TagsFilter {
public key: string
@ -122,6 +123,7 @@ export class Tag extends TagsFilter {
/**
*
* import {RegexTag} from "./RegexTag";
* import {And} from "./And";
*
* // should handle advanced regexes
* new Tag("key", "aaa").shadows(new RegexTag("key", /a+/)) // => true
@ -131,14 +133,20 @@ export class Tag extends TagsFilter {
* new Tag("key","value").shadows(new RegexTag("key", "value", true)) // => false
* new Tag("key","value").shadows(new RegexTag("otherkey", "value", true)) // => false
* new Tag("key","value").shadows(new RegexTag("otherkey", "value", false)) // => false
* new Tag("key","value").shadows(new And([new Tag("x","y"), new RegexTag("a","b", true)]) // => false
*/
shadows(other: TagsFilter): boolean {
if (other["key"] !== undefined) {
if (other["key"] !== this.key) {
return false
}
if ((other["key"] !== this.key)) {
return false
}
return other.matchesProperties({ [this.key]: this.value })
if(other instanceof Tag){
// Other.key === this.key
return other.value === this.value
}
if(other instanceof RegexTag){
return other.matchesProperties({[this.key]: this.value})
}
return false
}
usedKeys(): string[] {

View file

@ -27,23 +27,23 @@ export default class LinkedDataLoader {
opening_hours: { "@id": "http://schema.org/openingHoursSpecification" },
openingHours: { "@id": "http://schema.org/openingHours", "@container": "@set" },
geo: { "@id": "http://schema.org/geo" },
alt_name: { "@id": "http://schema.org/alternateName" },
alt_name: { "@id": "http://schema.org/alternateName" }
}
private static COMPACTING_CONTEXT_OH = {
dayOfWeek: { "@id": "http://schema.org/dayOfWeek", "@container": "@set" },
closes: {
"@id": "http://schema.org/closes",
"@type": "http://www.w3.org/2001/XMLSchema#time",
"@type": "http://www.w3.org/2001/XMLSchema#time"
},
opens: {
"@id": "http://schema.org/opens",
"@type": "http://www.w3.org/2001/XMLSchema#time",
},
"@type": "http://www.w3.org/2001/XMLSchema#time"
}
}
private static formatters: Record<"phone" | "email" | "website", Validator> = {
phone: new PhoneValidator(),
email: new EmailValidator(),
website: new UrlValidator(undefined, undefined, true),
website: new UrlValidator(undefined, undefined, true)
}
private static ignoreKeys = [
"http://schema.org/logo",
@ -56,7 +56,7 @@ export default class LinkedDataLoader {
"http://schema.org/description",
"http://schema.org/hasMap",
"http://schema.org/priceRange",
"http://schema.org/contactPoint",
"http://schema.org/contactPoint"
]
private static shapeToPolygon(str: string): Polygon {
@ -69,8 +69,8 @@ export default class LinkedDataLoader {
.trim()
.split(" ")
.map((n) => Number(n))
),
],
)
]
}
}
@ -92,18 +92,18 @@ export default class LinkedDataLoader {
const context = {
lat: {
"@id": "http://schema.org/latitude",
"@type": "http://www.w3.org/2001/XMLSchema#double",
"@type": "http://www.w3.org/2001/XMLSchema#double"
},
lon: {
"@id": "http://schema.org/longitude",
"@type": "http://www.w3.org/2001/XMLSchema#double",
},
"@type": "http://www.w3.org/2001/XMLSchema#double"
}
}
const flattened = await jsonld.compact(geo, context)
return {
type: "Point",
coordinates: [Number(flattened.lon), Number(flattened.lat)],
coordinates: [Number(flattened.lon), Number(flattened.lat)]
}
}
@ -194,7 +194,7 @@ export default class LinkedDataLoader {
)
delete compacted["openingHours"]
}
if(compacted["opening_hours"] === undefined){
if (compacted["opening_hours"] === undefined) {
delete compacted["opening_hours"]
}
if (compacted["geo"]) {
@ -288,7 +288,7 @@ export default class LinkedDataLoader {
if (properties["latitude"] && properties["longitude"]) {
geometry = {
type: "Point",
coordinates: [Number(properties["longitude"]), Number(properties["latitude"])],
coordinates: [Number(properties["longitude"]), Number(properties["latitude"])]
}
delete properties["latitude"]
delete properties["longitude"]
@ -300,7 +300,7 @@ export default class LinkedDataLoader {
const geo: GeoJSON = {
type: "Feature",
properties,
geometry,
geometry
}
delete linkedData.geo
delete properties.shape
@ -331,7 +331,7 @@ export default class LinkedDataLoader {
return
}
output[key] = output[key].map((v) => applyF(v))
if(!output[key].some(v => v !== undefined)){
if (!output[key].some(v => v !== undefined)) {
delete output[key]
}
}
@ -416,7 +416,7 @@ export default class LinkedDataLoader {
"brede publiek",
"iedereen",
"bezoekers",
"iedereen - vooral bezoekers gemeentehuis of bibliotheek.",
"iedereen - vooral bezoekers gemeentehuis of bibliotheek."
].indexOf(audience.toLowerCase()) >= 0
) {
return "yes"
@ -483,7 +483,6 @@ export default class LinkedDataLoader {
}
rename("capacityElectric", "capacity:electric_bicycle")
delete output["name"]
delete output["numberOfLevels"]
return output
@ -500,13 +499,14 @@ export default class LinkedDataLoader {
mv: "http://schema.mobivoc.org/",
gr: "http://purl.org/goodrelations/v1#",
vp: "https://data.velopark.be/openvelopark/vocabulary#",
vpt: "https://data.velopark.be/openvelopark/terms#",
vpt: "https://data.velopark.be/openvelopark/terms#"
},
[url],
undefined,
" ?parking a <http://schema.mobivoc.org/BicycleParkingStation>",
"?parking " + property + " " + (variable ?? "")
)
console.log("Fetching a velopark property gave", property, results)
return results
}
@ -521,7 +521,7 @@ export default class LinkedDataLoader {
mv: "http://schema.mobivoc.org/",
gr: "http://purl.org/goodrelations/v1#",
vp: "https://data.velopark.be/openvelopark/vocabulary#",
vpt: "https://data.velopark.be/openvelopark/terms#",
vpt: "https://data.velopark.be/openvelopark/terms#"
},
[url],
"g",
@ -643,36 +643,41 @@ export default class LinkedDataLoader {
allPartialResults.push(r)
}
const results = this.mergeResults(...allPartialResults)
return results
return this.mergeResults(...allPartialResults)
}
private static veloparkCache : Record<string, Feature[]> = {}
private static veloparkCache: Record<string, Feature[]> = {}
/**
* Fetches all data relevant to velopark.
* The id will be saved as `ref:velopark`
* @param url
*/
public static async fetchVeloparkEntry(url: string): Promise<Feature[]> {
if(this.veloparkCache[url]){
return this.veloparkCache[url]
public static async fetchVeloparkEntry(url: string, includeExtras: boolean = false): Promise<Feature[]> {
const cacheKey = includeExtras + url
if (this.veloparkCache[cacheKey]) {
return this.veloparkCache[cacheKey]
}
const withProxyUrl = Constants.linkedDataProxy.replace("{url}", encodeURIComponent(url))
const optionalPaths: Record<string, string | Record<string, string>> = {
"schema:interactionService": {
"schema:url": "website",
"schema:url": "website"
},
"schema:name": "name",
"mv:operatedBy": {
"gr:legalName": "operator",
"gr:legalName": "operator"
},
"schema:contactPoint": {
"schema:email": "email",
"schema:telephone": "phone",
"schema:telephone": "phone"
},
"schema:dateModified": "_last_edit_timestamp",
"schema:dateModified": "_last_edit_timestamp"
}
if (includeExtras) {
optionalPaths["schema:address"] = {
"schema:streetAddress": "addr"
}
optionalPaths["schema:name"] = "name"
optionalPaths["schema:description"] = "description"
}
const graphOptionalPaths = {
@ -687,19 +692,19 @@ export default class LinkedDataLoader {
"schema:geo": {
"schema:latitude": "latitude",
"schema:longitude": "longitude",
"schema:polygon": "shape",
"schema:polygon": "shape"
},
"schema:priceSpecification": {
"mv:freeOfCharge": "fee",
"schema:price": "charge",
},
"schema:price": "charge"
}
}
const extra = [
"schema:priceSpecification [ mv:dueForTime [ mv:timeStartValue ?chargeStart; mv:timeEndValue ?chargeEnd; mv:timeUnit ?timeUnit ] ]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#CargoBicycle>; vp:bicyclesAmount ?capacityCargobike; vp:bicycleType ?cargoBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#ElectricBicycle>; vp:bicyclesAmount ?capacityElectric; vp:bicycleType ?electricBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#TandemBicycle>; vp:bicyclesAmount ?capacityTandem; vp:bicycleType ?tandemBikeType]",
"vp:allows [vp:bicycleType <https://data.velopark.be/openvelopark/terms#TandemBicycle>; vp:bicyclesAmount ?capacityTandem; vp:bicycleType ?tandemBikeType]"
]
const unpatched = await this.fetchEntry(
@ -714,7 +719,7 @@ export default class LinkedDataLoader {
p["ref:velopark"] = [section]
patched.push(LinkedDataLoader.asGeojson(p))
}
this.veloparkCache[url] = patched
this.veloparkCache[cacheKey] = patched
return patched
}
}

View file

@ -5,6 +5,8 @@ import type { Feature, FeatureCollection, MultiPolygon } from "geojson"
import * as turf from "@turf/turf"
import { Utils } from "../../Utils"
import TagInfo from "./TagInfo"
import type { Feature, MultiPolygon } from "geojson"
import * as turf from "@turf/turf"
/**
* Main name suggestion index file
@ -140,6 +142,7 @@ export default class NameSuggestionIndex {
return true
}
const resolvedSet = NameSuggestionIndex.loco.resolveLocationSet(i.locationSet)
if (resolvedSet) {
// We actually have a location set, so we can check if the feature is in it, by determining if our point is inside the MultiPolygon using @turf/boolean-point-in-polygon
// This might occur for some extra boundaries, such as counties, ...

View file

@ -167,7 +167,7 @@ export class Denomination {
* Returns null if it doesn't match this unit
*/
public StrippedValue(value: string, actAsDefault: boolean, inverted: boolean): string {
if (value === undefined) {
if (value === undefined || value === "") {
return undefined
}
@ -186,7 +186,7 @@ export class Denomination {
if (self.prefix) {
return value.substring(key.length).trim()
}
let trimmed = value.substring(0, value.length - key.length).trim()
let trimmed = value.substring(0, value.length - key.length).trim()
if(!inverted){
return trimmed
}

View file

@ -100,6 +100,11 @@ export class MenuState {
})
this.allToggles = [
{
toggle: this.privacyPanelIsOpened,
name: "privacy",
showOverOthers: true,
},
{
toggle: this.menuIsOpened,
name: "menu",
@ -120,11 +125,6 @@ export class MenuState {
name: "community",
showOverOthers: true,
},
{
toggle: this.privacyPanelIsOpened,
name: "privacy",
showOverOthers: true,
},
{
toggle: this.filtersPanelIsOpened,
name: "filters",

View file

@ -258,11 +258,11 @@ export class ExpandRewrite<T> extends Conversion<T | RewritableConfigJson<T>, T[
}
}
let renderings = Array.isArray(rewrite.renderings)
const renderings = Array.isArray(rewrite.renderings)
? rewrite.renderings
: [rewrite.renderings]
for (let i = 0; i < keysToRewrite.into.length; i++) {
let ts: T[] = <T[]>Utils.Clone(renderings)
const ts: T[] = <T[]>Utils.Clone(renderings)
for (const tx of ts) {
let t = <T>tx
const sourceKeysToIgnore: string[] = []

View file

@ -161,7 +161,9 @@ class ExpandTagRendering extends Conversion<
private readonly _options: {
/* If true, will copy the 'osmSource'-tags into the condition */
applyCondition?: true | boolean
noHardcodedStrings?: false | boolean
noHardcodedStrings?: false | boolean,
addToContext?: false | boolean
}
constructor(
@ -169,11 +171,13 @@ class ExpandTagRendering extends Conversion<
self: LayerConfigJson,
options?: {
applyCondition?: true | boolean
noHardcodedStrings?: false | boolean
noHardcodedStrings?: false | boolean,
// If set, a question will be added to the 'sharedTagRenderings'. Should only be used for 'questions.json'
addToContext?: false | boolean
}
) {
super(
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question",
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question and reusing the builtins",
[],
"ExpandTagRendering"
)
@ -204,8 +208,17 @@ class ExpandTagRendering extends Conversion<
if (typeof tr === "string" || tr["builtin"] !== undefined) {
const stable = this.convert(tr, ctx.inOperation("recursive_resolve"))
result.push(...stable)
if(this._options?.addToContext){
for (const tr of stable) {
this._state.tagRenderings?.set(tr.id, tr)
}
}
} else {
result.push(tr)
if(this._options?.addToContext){
this._state.tagRenderings?.set(tr["id"], <QuestionableTagRenderingConfigJson> tr)
}
}
}
@ -220,7 +233,7 @@ class ExpandTagRendering extends Conversion<
}
const result: TagRenderingConfigJson[] = []
for (const tagRenderingConfigJson of direct) {
let nm: string | string[] | undefined = tagRenderingConfigJson["builtin"]
const nm: string | string[] | undefined = tagRenderingConfigJson["builtin"]
if (nm !== undefined) {
let indirect: TagRenderingConfigJson[]
if (typeof nm === "string") {
@ -1261,12 +1274,14 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
}
export class PrepareLayer extends Fuse<LayerConfigJson> {
constructor(state: DesugaringContext) {
constructor(state: DesugaringContext, options?: {addTagRenderingsToContext?: false | boolean}) {
super(
"Fully prepares and expands a layer for the LayerConfig.",
new On("tagRenderings", new Each(new RewriteSpecial())),
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer))),
new On("tagRenderings", (layer) => new Concat(new ExpandTagRendering(state, layer, {
addToContext: options?.addTagRenderingsToContext ?? false
}))),
new On("tagRenderings", new Each(new DetectInline())),
new AddQuestionBox(),
new AddEditingElements(state),

View file

@ -562,6 +562,65 @@ export class DetectNonErasedKeysInMappings extends DesugaringStep<QuestionableTa
}
}
export class DetectMappingsShadowedByCondition extends DesugaringStep<TagRenderingConfigJson> {
private readonly _forceError: boolean
constructor(forceError: boolean = false) {
super("Checks that, if the tagrendering has a condition, that a mapping is not contradictory to it, i.e. that there are no dead mappings", [], "DetectMappingsShadowedByCondition")
this._forceError = forceError
}
/**
*
* const validator = new DetectMappingsShadowedByCondition(true)
* const ctx = ConversionContext.construct([],["test"])
* validator.convert({
* condition: "count>0",
* mappings:[
* {
* if: "count=0",
* then:{
* en: "No count"
* }
* }
* ]
* }, ctx)
* ctx.hasErrors() // => true
*/
convert(json: TagRenderingConfigJson, context: ConversionContext): TagRenderingConfigJson {
if(!json.condition && !json.metacondition){
return json
}
if(!json.mappings || json.mappings?.length ==0){
return json
}
let conditionJson = json.condition ?? json.metacondition
if(json.condition !== undefined && json.metacondition !== undefined){
conditionJson = {and: [json.condition, json.metacondition]}
}
const condition = TagUtils.Tag(conditionJson, context.path.join("."))
for (let i = 0; i < json.mappings.length; i++){
const mapping = json.mappings[i]
const tagIf = TagUtils.Tag(mapping.if, context.path.join("."))
const optimized = new And([tagIf, condition]).optimize()
if(optimized === false){
const msg = ("Detected a conflicting mapping and condition. The mapping requires tags " + tagIf.asHumanString() + ", yet this can never happen because the set condition requires " + condition.asHumanString())
const ctx = context.enters("mappings", i)
if (this._forceError) {
ctx.err(msg)
} else {
ctx.warn(msg)
}
}
}
return undefined
}
}
export class DetectShadowedMappings extends DesugaringStep<TagRenderingConfigJson> {
private readonly _calculatedTagNames: string[]
@ -1088,6 +1147,8 @@ export class ValidateTagRenderings extends Fuse<TagRenderingConfigJson> {
"Various validation on tagRenderingConfigs",
new MiscTagRenderingChecks(),
new DetectShadowedMappings(layerConfig),
new DetectMappingsShadowedByCondition(),
new DetectConflictingAddExtraTags(),
// TODO enable new DetectNonErasedKeysInMappings(),
new DetectMappingsWithImages(doesImageExist),

View file

@ -554,4 +554,15 @@ export interface LayerConfigJson {
* group: hidden
*/
fullNodeDatabase?: boolean
/**
* question: Should a theme using this layer leak some location info when making changes?
*
* When a changeset is made, a 'distance to object'-class is written to the changeset.
* For some particular themes and layers, this might leak too much information, and we want to obfuscate this
*
* ifunset: Write 'change_within_x_m' as usual and if GPS is enabled
* iftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey
*/
enableMorePrivacy?: boolean
}

View file

@ -439,4 +439,16 @@ export interface LayoutConfigJson {
* group: hidden
*/
enableNodeDatabase?: boolean
/**
* question: Should this theme leak some location info when making changes?
*
* When a changeset is made, a 'distance to object'-class is written to the changeset.
* For some particular themes and layers, this might leak too much information, and we want to obfuscate this
*
* ifunset: Write 'change_within_x_m' as usual and if GPS is enabled
* iftrue: Do not write 'change_within_x_m' and do not indicate that this was done by survey
*/
enableMorePrivacy: boolean
}

View file

@ -229,6 +229,7 @@ export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJs
* A (translated) text that is shown (as gray text) within the textfield
* type: translation
* group: expert
* ifunset: No specific placeholder is set, show the type of the textfield
*/
placeholder?: Translatable

View file

@ -88,7 +88,7 @@ export interface TagRenderingConfigJson {
*
* question: When should this item be shown?
* type: tag
* ifunset: No specific condition set; always show this tagRendering or ask the question if unkown
* ifunset: No specific condition set; always show this tagRendering or show this question if unknown
*
* Only show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.
*
@ -132,9 +132,10 @@ export interface TagRenderingConfigJson {
/** question: When should this item be shown (including special conditions)?
* type: tag
* ifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown
*
* If set, this tag will be evaluated agains the _usersettings/application state_ table.
* Enable 'show debug info' in user settings to see available options.
* If set, this tag will be evaluated against the _usersettings/application state_ table.
* Enable 'show debug info' in user settings to see available options (at the settings-tab).
* Note that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_
*/
metacondition?: TagConfigJson

View file

@ -67,6 +67,7 @@ export default class LayerConfig extends WithContextLoader {
public readonly _needsFullNodeDatabase: boolean
public readonly popupInFloatover: boolean | string
public readonly enableMorePrivacy: boolean
constructor(json: LayerConfigJson, context?: string, official: boolean = true) {
context = context + "." + json.id
@ -149,6 +150,7 @@ export default class LayerConfig extends WithContextLoader {
this.shownByDefault = json.shownByDefault ?? true
this.doCount = json.isCounted ?? this.shownByDefault ?? true
this.forceLoad = json.forceLoad ?? false
this.enableMorePrivacy = json.enableMorePrivacy ?? false
if (json.presets === null) json.presets = undefined
if (json.presets !== undefined && json.presets?.map === undefined) {
throw "Presets should be a list of items (at " + context + ")"

View file

@ -63,6 +63,8 @@ export default class LayoutConfig implements LayoutInformation {
public readonly enableExportButton: boolean
public readonly enablePdfDownload: boolean
public readonly enableTerrain: boolean
public readonly enableMorePrivacy: boolean
public readonly customCss?: string
@ -204,6 +206,7 @@ export default class LayoutConfig implements LayoutInformation {
this.overpassTimeout = json.overpassTimeout ?? 30
this.overpassMaxZoom = json.overpassMaxZoom ?? 16
this.osmApiTileSize = json.osmApiTileSize ?? this.overpassMaxZoom + 1
this.enableMorePrivacy = json.enableMorePrivacy || json.layers.some(l => (<LayerConfigJson> l).enableMorePrivacy)
this.layersDict = new Map<string, LayerConfig>()
for (const layer of this.layers) {

View file

@ -69,6 +69,7 @@ import {
import summaryLayer from "../assets/generated/layers/summary.json"
import { LayerConfigJson } from "./ThemeConfig/Json/LayerConfigJson"
import Locale from "../UI/i18n/Locale"
import Hash from "../Logic/Web/Hash"
/**
*
@ -265,6 +266,7 @@ export default class ThemeViewState implements SpecialVisualizationState {
featurePropertiesStore: this.featureProperties,
osmConnection: this.osmConnection,
historicalUserLocations: this.geolocation.historicalUserLocations,
featureSwitches: this.featureSwitches
},
layout?.isLeftRightSensitive() ?? false
)
@ -495,6 +497,12 @@ export default class ThemeViewState implements SpecialVisualizationState {
if (this.layout.customCss !== undefined && window.location.pathname.indexOf("theme") >= 0) {
Utils.LoadCustomCss(this.layout.customCss)
}
Hash.hash.addCallbackAndRunD(hash => {
if(hash === "current_view" || hash.match(/current_view_[0-9]+/)){
this.selectCurrentView()
}
})
}
/**
@ -820,4 +828,9 @@ export default class ThemeViewState implements SpecialVisualizationState {
this.userRelatedState.preferredBackgroundLayer
)
}
public selectCurrentView(){
this.guistate.closeAll()
this.selectedElement.setData(this.currentView.features?.data?.[0])
}
}

View file

@ -100,7 +100,7 @@
<input
autofocus
bind:value={$themeSearchText}
class="mr-4 w-full"
class="mr-4 w-full outline-none"
id="theme-search"
type="search"
use:placeholder={tr.searchForATheme}

View file

@ -0,0 +1,49 @@
<script lang="ts">
import { Store } from "../../Logic/UIEventSource"
import { onDestroy, onMount } from "svelte"
let elem: HTMLElement
let targetOuter: HTMLElement
export let isOpened: Store<boolean>
export let moveTo: Store<HTMLElement>
export let debug : string
function copySizeOf(htmlElem: HTMLElement) {
const target = htmlElem.getBoundingClientRect()
elem.style.left = target.x + "px"
elem.style.top = target.y + "px"
elem.style.width = target.width + "px"
elem.style.height = target.height + "px"
}
function animate(opened: boolean) {
const moveToElem = moveTo.data
console.log("Animating", debug," to", opened)
if (opened) {
copySizeOf(targetOuter)
elem.style.background = "var(--background-color)"
} else if (moveToElem !== undefined) {
copySizeOf(moveToElem)
elem.style.background = "#ffffff00"
} else {
elem.style.left = "0px"
elem.style.top = "0px"
elem.style.background = "#ffffff00"
}
}
onDestroy(isOpened.addCallback(opened => animate(opened)))
onMount(() => requestAnimationFrame(() => animate(isOpened.data)))
</script>
<div class={"absolute bottom-0 right-0 h-full w-screen p-4 md:p-6 pointer-events-none invisible"}>
<div class="content h-full" bind:this={targetOuter} style="background: red" />
</div>
<div bind:this={elem} class="pointer-events-none absolute bottom-0 right-0 low-interaction rounded-2xl"
style="transition: all 0.5s ease-out, background-color 1.4s ease-out; background: var(--background-color);">
<!-- Classes should be the same as the 'floatoaver' -->
</div>

View file

@ -11,7 +11,6 @@
*/
const dispatch = createEventDispatcher<{ close }>()
export let extraClasses = "p-4 md:p-6"
</script>
<!-- Draw the background over the total screen -->
@ -24,7 +23,7 @@
/>
<!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers -->
<div
class={twMerge("absolute bottom-0 right-0 h-full w-screen", extraClasses)}
class={"absolute bottom-0 right-0 h-full w-screen p-4 md:p-6"}
style="z-index: 21"
use:trapFocus
>

View file

@ -3,7 +3,7 @@
import { twJoin } from "tailwind-merge"
import { Translation } from "../i18n/Translation"
import { ariaLabel } from "../../Utils/ariaLabel"
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
/**
* A round button with an icon and possible a small text, which hovers above the map
@ -12,9 +12,15 @@
export let cls = "m-0.5 p-0.5 sm:p-1 md:m-1"
export let enabled: Store<boolean> = new ImmutableStore(true)
export let arialabel: Translation = undefined
export let htmlElem: UIEventSource<HTMLElement> = undefined
let _htmlElem : HTMLElement
$: {
htmlElem?.setData(_htmlElem)
}
</script>
<button
bind:this={_htmlElem}
on:click={(e) => dispatch("click", e)}
on:keydown
use:ariaLabel={arialabel}

View file

@ -1,11 +1,8 @@
import Combine from "./Combine"
import BaseUIElement from "../BaseUIElement"
import Title from "./Title"
import List from "./List"
import Link from "./Link"
import { marked } from "marked"
import { parse as parse_html } from "node-html-parser"
import {default as turndown} from "turndown"
import { default as turndown } from "turndown"
import { Utils } from "../../Utils"
export default class TableOfContents {
@ -56,7 +53,7 @@ export default class TableOfContents {
const htmlSource = <string>marked.parse(md)
const el = parse_html(htmlSource)
const structure = TableOfContents.generateStructure(<any>el)
let firstTitle = structure[1]
const firstTitle = structure[1]
let minDepth = undefined
do {
minDepth = Math.min(...structure.map(s => s.depth))
@ -81,7 +78,7 @@ export default class TableOfContents {
let topLevelCount = 0
for (const el of structure) {
const depthDiff = el.depth - minDepth
let link = `[${el.title}](#${TableOfContents.asLinkableId(el.title)})`
const link = `[${el.title}](#${TableOfContents.asLinkableId(el.title)})`
if (depthDiff === 0) {
topLevelCount++
toc += `${topLevelCount}. ${link}\n`
@ -91,16 +88,14 @@ export default class TableOfContents {
}
const heading = Utils.Times(() => "#", firstTitle.depth)
toc = heading +" Table of contents\n\n"+toc
toc = heading + " Table of contents\n\n" + toc
const original = el.outerHTML
const firstTitleIndex = original.indexOf(firstTitle.el.outerHTML)
const tocHtml = (<string>marked.parse(toc))
const withToc = original.substring(0, firstTitleIndex) + tocHtml + original.substring(firstTitleIndex)
const firstTitleIndex = md.indexOf(firstTitle.title)
const htmlToMd = new turndown()
return htmlToMd.turndown(withToc)
const intro = md.substring(0, firstTitleIndex)
const splitPoint = intro.lastIndexOf("\n")
return md.substring(0, splitPoint) + toc + md.substring(splitPoint)
}

View file

@ -12,7 +12,6 @@
export let state: SpecialVisualizationState
let theme = state.layout?.id ?? ""
let config: ExtraLinkConfig = state.layout.extraLink
console.log(">>>",config)
const isIframe = window !== window.top
let basepath = window.location.host
let showWelcomeMessageSwitch = state.featureSwitches.featureSwitchWelcomeMessage

View file

@ -113,7 +113,7 @@
{:else}
<input
type="search"
class="w-full"
class="w-full outline-none"
bind:this={inputElement}
on:keypress={(keypr) => {
feedback = undefined

View file

@ -4,7 +4,6 @@
* Even though the component is very small, it gets its own class as it is often reused
*/
import { Square3Stack3dIcon } from "@babeard/svelte-heroicons/solid"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import Translations from "../i18n/Translations"
import MapControlButton from "../Base/MapControlButton.svelte"
import Tr from "../Base/Tr.svelte"
@ -16,11 +15,13 @@
export let state: ThemeViewState
export let map: Store<MlMap> = undefined
export let hideTooltip = false
export let htmlElem : UIEventSource<HTMLElement> = undefined
</script>
<MapControlButton
arialabel={Translations.t.general.labels.background}
on:click={() => state.guistate.backgroundLayerSelectionIsOpened.setData(true)}
{htmlElem}
>
<StyleLoadingIndicator map={map ?? state.map} rasterLayer={state.mapProperties.rasterLayer}>
<Square3Stack3dIcon class="h-6 w-6" />

View file

@ -1,8 +1,15 @@
<script lang="ts">
import Tr from "../Base/Tr.svelte"
import Translations from "../i18n/Translations"
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import UserRelatedState from "../../Logic/State/UserRelatedState"
const t = Translations.t.privacy
export let state: SpecialVisualizationState
const usersettings = UserRelatedState.usersettingsConfig
const editPrivacy = usersettings.tagRenderings.find(tr => tr.id === "more_privacy")
const isLoggedIn = state.osmConnection.isLoggedIn
</script>
<div class="link-underline flex flex-col">
@ -20,7 +27,41 @@
<h3>
<Tr t={t.editingTitle} />
</h3>
<Tr t={t.editing} />
<Tr t={t.editingIntro} />
<Tr t={t.editingIntro} />
<ul>
<li>
<Tr t={t.items.changesYouMake} />
</li>
<li>
<Tr t={t.items.username} />
</li>
<li>
<Tr t={t.items.date} />
</li>
<li>
<Tr t={t.items.theme} />
</li>
<li>
<Tr t={t.items.language} />
</li>
<li>
{#if $isLoggedIn}
<TagRenderingEditable config={editPrivacy} selectedElement={{
type: "Feature",
properties: { id: "settings" },
geometry: { type: "Point", coordinates: [0, 0] },
}}
{state}
tags={state.userRelatedState.preferencesAsTags} />
{:else}
<Tr t={t.items.distanceIndicator} />
{/if}
</li>
</ul>
<Tr t={t.editingOutro} />
<h3>
<Tr t={t.miscCookiesTitle} />

View file

@ -15,6 +15,7 @@
import Add from "../../assets/svg/Add.svelte"
import Location_refused from "../../assets/svg/Location_refused.svelte"
import Location from "../../assets/svg/Location.svelte"
import SpecialTranslation from "../Popup/TagRendering/SpecialTranslation.svelte"
/**
* The theme introduction panel
@ -48,6 +49,7 @@
<div class="flex h-full flex-col justify-between">
<div>
<!-- Intro, description, ... -->
<Tr t={layout.description} />
<Tr t={Translations.t.general.welcomeExplanation.general} />
{#if layout.layers.some((l) => l.presets?.length > 0)}

View file

@ -109,16 +109,17 @@
<div class="low-interaction border-interactive p-1">
{#if !readonly}
<Tr t={t.loadedFrom.Subs({ url: sourceUrl, source: sourceUrl })} />
<h3>
<Tr t={t.conflicting.title} />
</h3>
{/if}
<div class="flex flex-col" class:gap-y-8={!readonly}>
{#if !readonly}
<Tr t={t.conflicting.intro} />
{/if}
{#if $different.length > 0}
{#if !readonly}
<h3>
<Tr t={t.conflicting.title} />
</h3>
<Tr t={t.conflicting.intro} />
{/if}
{#each $different as key (key)}
<div class="mx-2 rounded-2xl">
<ComparisonAction
@ -135,6 +136,13 @@
{/if}
{#if $missing.length > 0}
{#if !readonly}
<h3 class="m-0">
<Tr t={t.missing.title} />
</h3>
<Tr t={t.missing.intro} />
{/if}
{#if currentStep === "init"}
{#each $missing as key (key)}
<div class:glowing-shadow={applyAllHovered} class="mx-2 rounded-2xl">

View file

@ -27,9 +27,9 @@
addExtraTags = helperArgs[1].split(";")
}
const path = `${key}s/${maintag.split("=")[0]}/${maintag.split("=")[1]}`
const [k, v] = maintag.split("=")
let items: NSIItem[] = NameSuggestionIndex.getSuggestionsFor(path, feature.properties["_country"], GeoOperations.centerpointCoordinates(feature))
let items: NSIItem[] = NameSuggestionIndex.getSuggestionsFor(key, k, v, feature.properties["_country"], GeoOperations.centerpointCoordinates(feature))
let selectedItem: NSIItem = items.find((item) => item.tags[key] === value.data)
@ -113,10 +113,10 @@
})
</script>
{#if items?.length >= 0}
<div>
<div class="normal-background my-2 flex w-5/6 justify-between rounded-full pl-2">
<!-- TODO translate placeholder -->
<input type="text" placeholder="Filter entries" bind:value={filter} class="outline-none" />
<SearchIcon aria-hidden="true" class="h-6 w-6 self-end" />
</div>

View file

@ -19,6 +19,7 @@
import OpeningHoursInput from "./Helpers/OpeningHoursInput.svelte"
import SlopeInput from "./Helpers/SlopeInput.svelte"
import type { SpecialVisualizationState } from "../SpecialVisualization"
import NameSuggestionIndexInput from "./Helpers/NameSuggestionIndexInput.svelte"
export let type: ValidatorType
export let value: UIEventSource<string | object>
@ -26,6 +27,9 @@
export let feature: Feature
export let args: (string | number | boolean)[] = undefined
export let state: SpecialVisualizationState
export let helperArgs: (string | number | boolean)[]
export let key: string
export let extraTags: UIEventSource<Record<string, string>>
let properties = { feature, args: args ?? [] }
</script>
@ -50,4 +54,6 @@
<SlopeInput {value} {feature} {state} />
{:else if type === "wikidata"}
<ToSvelte construct={() => InputHelpers.constructWikidataHelper(value, properties)} />
{:else if type === "nsi"}
<NameSuggestionIndexInput {value} {feature} {helperArgs} {key} {extraTags} />
{/if}

View file

@ -28,6 +28,7 @@ import TagValidator from "./Validators/TagValidator"
import IdValidator from "./Validators/IdValidator"
import SlopeValidator from "./Validators/SlopeValidator"
import VeloparkValidator from "./Validators/VeloparkValidator"
import NameSuggestionIndexValidator from "./Validators/NameSuggestionIndexValidator"
import CurrencyValidator from "./Validators/CurrencyValidator"
export type ValidatorType = (typeof Validators.availableTypes)[number]
@ -61,6 +62,7 @@ export default class Validators {
"id",
"slope",
"velopark",
"nsi",
"currency"
] as const
@ -91,6 +93,7 @@ export default class Validators {
new IdValidator(),
new SlopeValidator(),
new VeloparkValidator(),
new NameSuggestionIndexValidator(),
new CurrencyValidator()
]

View file

@ -0,0 +1,40 @@
import Title from "../../Base/Title"
import Combine from "../../Base/Combine"
import { Validator } from "../Validator"
import Table from "../../Base/Table"
export default class NameSuggestionIndexValidator extends Validator {
constructor() {
super(
"nsi",
new Combine([
"Gives a list of possible suggestions for a brand or operator tag.",
new Title("Helper arguments"),
new Table(
["name", "doc"],
[
[
"options",
new Combine([
"A JSON-object of type `{ main: string, key: string }`. ",
new Table(
["subarg", "doc"],
[
[
"main",
"The main tag to give suggestions for, e.g. `amenity=restaurant`.",
],
[
"addExtraTags",
"Extra tags to add to the suggestions, e.g. `nobrand=yes`.",
],
]
),
]),
],
]
),
])
)
}
}

View file

@ -1,11 +1,11 @@
<script lang="ts">
import Loading from "../Base/Loading.svelte"
import { Stores, UIEventSource } from "../../Logic/UIEventSource"
import { Store, Stores, UIEventSource } from "../../Logic/UIEventSource"
import { Map as MlMap } from "maplibre-gl"
import { onDestroy } from "svelte"
let isLoading = false
export let map: UIEventSource<MlMap>
export let map: Store<MlMap>
/**
* Optional. Only used for the 'global' change indicator so that it won't spin on pan/zoom but only when a change _actually_ occured
*/

View file

@ -216,7 +216,7 @@ class ApplyButton extends UIElement {
}
export default class AutoApplyButton implements SpecialVisualization {
public readonly docs: BaseUIElement
public readonly docs: string
public readonly funcName: string = "auto_apply"
public readonly needsUrls = []
@ -273,7 +273,7 @@ export default class AutoApplyButton implements SpecialVisualization {
"Then, use a calculated tag on the host feature to determine the overlapping object ids",
"At last, add this component",
]),
])
]).AsMarkdown()
}
constr(

View file

@ -15,6 +15,7 @@
export let unvalidatedText: UIEventSource<string> = new UIEventSource<string>(value.data)
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let extraTags: UIEventSource<Record<string, string>>
export let feature: Feature = undefined
export let state: SpecialVisualizationState
@ -27,6 +28,8 @@
inline = false
inline = config.freeform?.inline
}
let helperArgs = config.freeform?.helperArgs
let key = config.freeform?.key
const dispatch = createEventDispatcher<{ selected }>()
export let feedback: UIEventSource<Translation>
@ -75,6 +78,9 @@
type={config.freeform.type}
{value}
{state}
{helperArgs}
{key}
{extraTags}
on:submit
/>
</div>

View file

@ -79,7 +79,7 @@
console.log("Applying questions to ask")
const qta = questionsToAsk.data
firstQuestion.setData(undefined)
allQuestionsToAsk.setData([])
//allQuestionsToAsk.setData([])
await Utils.awaitAnimationFrame()
firstQuestion.setData(qta[0])
allQuestionsToAsk.setData(qta)

View file

@ -30,15 +30,18 @@
import { placeholder } from "../../../Utils/placeholder"
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
import { Tag } from "../../../Logic/Tags/Tag"
import { And } from "../../../Logic/Tags/And"
import { get } from "svelte/store"
import Markdown from "../../Base/Markdown.svelte"
export let config: TagRenderingConfig
export let tags: UIEventSource<Record<string, string>>
export let selectedElement: Feature
export let state: SpecialVisualizationState
export let layer: LayerConfig | undefined
export let selectedTags: UploadableTag = undefined
export let extraTags: UIEventSource<Record<string, string>> = new UIEventSource({})
export let allowDeleteOfFreeform: boolean = false
@ -69,6 +72,8 @@
/**
* Prepares and fills the checkedMappings
*/
console.log("Initing ", config.id)
function initialize(tgs: Record<string, string>, confg: TagRenderingConfig): void {
mappings = confg.mappings?.filter((m) => {
if (typeof m.hideInAnswer === "boolean") {
@ -76,8 +81,10 @@
}
return !m.hideInAnswer.matchesProperties(tgs)
})
selectedMapping = mappings?.findIndex(mapping => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs))
if(selectedMapping < 0){
selectedMapping = mappings?.findIndex(
(mapping) => mapping.if.matchesProperties(tgs) || mapping.alsoShowIf?.matchesProperties(tgs)
)
if (selectedMapping < 0) {
selectedMapping = undefined
}
// We received a new config -> reinit
@ -102,7 +109,7 @@
seenFreeforms.push(newProps[confg.freeform.key])
}
return matches
})
}),
]
if (tgs !== undefined && confg.freeform) {
@ -133,15 +140,35 @@
freeformInput.set(undefined)
}
feedback.setData(undefined)
}
$: {
// Even though 'config' is not declared as a store, Svelte uses it as one to update the component
// We want to (re)-initialize whenever the 'tags' or 'config' change - but not when 'checkedConfig' changes
initialize($tags, config)
}
let usedKeys: string[] = config.usedTags().flatMap((t) => t.usedKeys())
/**
* The 'minimalTags' is a subset of the tags of the feature, only containing the values relevant for this object.
* The main goal is to be stable and only 'ping' when an actual change is relevant
*/
let minimalTags = new UIEventSource<Record<string, string>>(undefined)
tags.addCallbackAndRunD((tags) => {
const previousMinimal = minimalTags.data
const newMinimal: Record<string, string> = {}
let somethingChanged = previousMinimal === undefined
for (const key of usedKeys) {
const newValue = tags[key]
somethingChanged ||= previousMinimal?.[key] !== newValue
if (newValue !== undefined && newValue !== null) {
newMinimal[key] = newValue
}
}
if (somethingChanged) {
console.log("Updating minimal tags to", newMinimal, "of", config.id)
minimalTags.setData(newMinimal)
}
})
minimalTags.addCallbackAndRunD((tgs) => {
initialize(tgs, config)
})
onDestroy(
freeformInput.subscribe((freeformValue) => {
if (!mappings || mappings?.length == 0 || config.freeform?.key === undefined) {
@ -178,23 +205,50 @@
checkedMappings,
tags.data
)
if(state.featureSwitches.featureSwitchIsDebugging.data){
console.log("Constructing change spec from", {freeform: $freeformInput, selectedMapping, checkedMappings, currentTags: tags.data}, " --> ", selectedTags)
if (featureSwitchIsDebugging?.data) {
console.log(
"Constructing change spec from",
{
freeform: $freeformInput,
selectedMapping,
checkedMappings,
currentTags: tags.data,
},
" --> ",
selectedTags
)
}
} catch (e) {
console.error("Could not calculate changeSpecification:", e)
selectedTags = undefined
}
}
if (extraTags.data) {
// Map the extraTags into an array of Tag objects
const extraTagsArray = Object.entries(extraTags.data).map(([k, v]) => new Tag(k, v))
// Check the type of selectedTags
if (selectedTags instanceof Tag) {
// Re-define selectedTags as an And
selectedTags = new And([selectedTags, ...extraTagsArray])
} else if (selectedTags instanceof And) {
// Add the extraTags to the existing And
selectedTags = new And([...selectedTags.and, ...extraTagsArray])
} else {
console.error("selectedTags is not of type Tag or And")
}
}
}
function onSave(_ = undefined) {
if (selectedTags === undefined) {
return
}
if (layer === undefined || (layer?.source === null && layer.id !== "favourite")) {
/**
* This is a special, priviliged layer.
* This is a special, privileged layer.
* We simply apply the tags onto the records
*/
const kv = selectedTags.asChange(tags.data)
@ -213,7 +267,7 @@
dispatch("saved", { config, applied: selectedTags })
const change = new ChangeTagAction(tags.data.id, selectedTags, tags.data, {
theme: tags.data["_orig_theme"] ?? state.layout.id,
changeType: "answer"
changeType: "answer",
})
freeformInput.set(undefined)
selectedMapping = undefined
@ -266,7 +320,7 @@
{#if config.questionhint}
{#if config.questionHintIsMd}
<Markdown srcWritable={ config.questionhint.current} />
<Markdown srcWritable={config.questionhint.current} />
{:else}
<div class="max-h-60 overflow-y-auto">
<SpecialTranslation
@ -277,7 +331,7 @@
feature={selectedElement}
/>
</div>
{/if}
{/if}
{/if}
</legend>
@ -301,6 +355,7 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}
@ -344,6 +399,7 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}
@ -388,6 +444,7 @@
{feedback}
{unit}
{state}
{extraTags}
feature={selectedElement}
value={freeformInput}
unvalidatedText={freeformInputUnvalidated}

View file

@ -6,6 +6,15 @@
import { Utils } from "../Utils"
import Add from "../assets/svg/Add.svelte"
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
import type { SpecialVisualizationState } from "./SpecialVisualization"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import UserRelatedState from "../Logic/State/UserRelatedState"
const osmConnection = new OsmConnection()
let state: SpecialVisualizationState = {
osmConnection,
userRelatedState: new UserRelatedState(osmConnection)
}
</script>
<div class="flex h-screen flex-col overflow-hidden px-4">
@ -17,7 +26,7 @@
<LanguagePicker availableLanguages={Translations.t.privacy.intro.SupportedLanguages()} />
</div>
<div class="h-full overflow-auto border border-gray-500 p-4">
<PrivacyPolicy />
<PrivacyPolicy {state} />
</div>
<a class="button flex" href={Utils.HomepageLink()}>
<Add class="h-6 w-6" />

View file

@ -1,12 +1,12 @@
import { Store, UIEventSource } from "../Logic/UIEventSource"
import BaseUIElement from "./BaseUIElement"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import { IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import { Changes } from "../Logic/Osm/Changes"
import { ExportableMap, MapProperties } from "../Models/MapProperties"
import LayerState from "../Logic/State/LayerState"
import { Feature, Geometry, Point } from "geojson"
import { Feature, Geometry, Point, Polygon } from "geojson"
import FullNodeDatabaseSource from "../Logic/FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { MangroveIdentity } from "../Logic/Web/MangroveReviews"
import { GeoIndexedStoreForLayer } from "../Logic/FeatureSource/Actors/GeoIndexedStore"
@ -61,8 +61,10 @@ export interface SpecialVisualizationState {
readonly selectedElement: UIEventSource<Feature>
readonly currentView: FeatureSource<Feature<Polygon>>
readonly favourites: FavouritesFeatureSource
/**
* If data is currently being fetched from external sources
*/

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@
export let configs: ConfigMeta[]
export let title: string | undefined = undefined
export let path: (string | number)[] = []
export let path: readonly (string | number)[] = []
let expertMode = state.expertMode
let configsNoHidden = configs.filter((schema) => schema.hints?.group !== "hidden")
@ -21,9 +21,9 @@
</script>
{#if configs === undefined}
Bug: 'Region' received 'undefined'
Bug: 'Region' received 'undefined' at {path.join(".")}
{:else if configs.length === 0}
Bug: Region received empty list as configuration
Bug: Region received empty list as configuration at {path.join(".")}
{:else if title}
<div class="flex w-full flex-col">
<h3>{title}</h3>

View file

@ -7,7 +7,7 @@
import type { ConfigMeta } from "./configMeta"
import type {
MappingConfigJson,
QuestionableTagRenderingConfigJson,
QuestionableTagRenderingConfigJson
} from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"
@ -59,8 +59,8 @@
labelMapping = {
if: "value=" + label,
then: {
en: "Builtin collection <b>" + label + "</b>:",
},
en: "Builtin collection <b>" + label + "</b>:"
}
}
perLabel[label] = labelMapping
mappingsBuiltin.push(labelMapping)
@ -72,14 +72,14 @@
mappingsBuiltin.push({
if: "value=" + tr["id"],
then: {
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>",
},
en: "Builtin <b>" + tr["id"] + "</b> <div class='subtle'>" + description + "</div>"
}
})
}
const configBuiltin = new TagRenderingConfig(<QuestionableTagRenderingConfigJson>{
question: "Which builtin element should be shown?",
mappings: mappingsBuiltin,
mappings: mappingsBuiltin
})
const tags = new UIEventSource({ value })
@ -112,7 +112,7 @@
"condition",
"metacondition",
"mappings",
"icon",
"icon"
])
const ignored = new Set(["labels", "description", "classes"])
@ -196,7 +196,10 @@
<h3>Text field and input element configuration</h3>
<div class="border-l border-dashed border-gray-800 pl-2">
<SchemaBasedField {state} path={[...path, "render"]} schema={topLevelItems["render"]} />
<Region {state} {path} configs={freeformSchema} />
{#if freeformSchema?.length > 0}
<!-- In read-only cases, (e.g. popup title) there will be no freeform-schema to set and thus freeformSchema will be undefined -->
<Region {state} {path} configs={freeformSchema} />
{/if}
<SchemaBasedField {state} path={[...path, "icon"]} schema={topLevelItems["icon"]} />
</div>

View file

@ -1,2 +1,3 @@
<script lang="ts">
</script>

View file

@ -18,7 +18,7 @@
EyeIcon,
HeartIcon,
MenuIcon,
XCircleIcon,
XCircleIcon
} from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
@ -73,6 +73,8 @@
import { BBox } from "../Logic/BBox"
import ReviewsOverview from "./Reviews/ReviewsOverview.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton.svelte"
import CloseAnimation from "./Base/CloseAnimation.svelte"
import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource"
export let state: ThemeViewState
let layout = state.layout
@ -138,7 +140,7 @@
const bottomRight = mlmap.unproject([rect.right, rect.bottom])
const bbox = new BBox([
[topLeft.lng, topLeft.lat],
[bottomRight.lng, bottomRight.lat],
[bottomRight.lng, bottomRight.lat]
])
state.visualFeedbackViewportBounds.setData(bbox)
}
@ -151,7 +153,7 @@
})
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let currentViewLayer = layout.layers.find((l) => l.id === "current_view")
let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
rasterLayer.data?.properties?.name ??
@ -183,6 +185,22 @@
const animation = mlmap.keyboard?.keydown(e)
animation?.cameraAnimation(mlmap)
}
/**
* Needed for the animations
*/
let openMapButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openMenuButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openCurrentViewLayerButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let _openNewElementButton: HTMLButtonElement
let openNewElementButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
$: {
openNewElementButton.setData(_openNewElementButton)
}
let openFilterButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openBackgroundButton : UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
</script>
<div class="absolute top-0 left-0 h-screen w-screen overflow-hidden">
@ -228,6 +246,7 @@
<MapControlButton
on:click={() => state.guistate.themeIsOpened.setData(true)}
on:keydown={forwardEventToMap}
htmlElem={openMapButton}
>
<div class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2">
<img
@ -245,15 +264,17 @@
arialabel={Translations.t.general.labels.menu}
on:click={() => state.guistate.menuIsOpened.setData(true)}
on:keydown={forwardEventToMap}
htmlElem={openMenuButton}
>
<MenuIcon class="h-8 w-8 cursor-pointer" />
</MapControlButton>
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
<MapControlButton
on:click={() => {
state.selectedElement.setData(state.currentView.features?.data?.[0])
state.selectCurrentView()
}}
on:keydown={forwardEventToMap}
htmlElem={openCurrentViewLayerButton}
>
<ToSvelte
construct={() => currentViewLayer.defaultIcon().SetClass("w-8 h-8 cursor-pointer")}
@ -287,6 +308,7 @@
<button
class="pointer-events-auto w-fit low-interaction"
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
bind:this={_openNewElementButton}
on:click={() => {
state.openNewDialog()
}}
@ -310,12 +332,13 @@
arialabel={Translations.t.general.labels.filter}
on:click={() => state.guistate.openFilterView()}
on:keydown={forwardEventToMap}
htmlElem={openFilterButton}
>
<Filter class="h-6 w-6" />
</MapControlButton>
</If>
<If condition={state.featureSwitches.featureSwitchBackgroundSelection}>
<OpenBackgroundSelectorButton hideTooltip={true} {state} />
<OpenBackgroundSelectorButton hideTooltip={true} {state} htmlElem={openBackgroundButton} />
</If>
<a
class="bg-black-transparent pointer-events-auto h-fit max-h-12 cursor-pointer self-end overflow-hidden rounded-2xl pl-1 pr-2 text-white opacity-50 hover:opacity-100"
@ -638,7 +661,7 @@
<Tr t={Translations.t.privacy.title} />
</h2>
<div class="overflow-auto p-4">
<PrivacyPolicy />
<PrivacyPolicy {state}/>
</div>
</div>
</FloatOver>
@ -657,3 +680,11 @@
</div>
</FloatOver>
</If>
<CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme"/>
<CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu"/>
<CloseAnimation isOpened={selectedLayer.map(sl => (sl !== undefined && sl === currentViewLayer))} moveTo={openCurrentViewLayerButton} debug="currentViewLayer"/>
<CloseAnimation isOpened={selectedElement.map(sl =>{ console.log("SE is", sl); return sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId })} moveTo={openNewElementButton} debug="newElement"/>
<CloseAnimation isOpened={state.guistate.filtersPanelIsOpened} moveTo={openFilterButton} debug="filter"/>
<CloseAnimation isOpened={state.guistate.backgroundLayerSelectionIsOpened} moveTo={openBackgroundButton} debug="bg"/>

View file

@ -70,8 +70,7 @@ export default class Locale {
}
if (!Utils.runningFromConsole) {
// @ts-ignore
window.setLanguage = function (language: string) {
window["setLanguage"] = function (language: string) {
source.setData(language)
}
}

View file

@ -1011,11 +1011,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
})
}
public static async downloadJsonCached(
public static async downloadJsonCached<T = object | []>(
url: string,
maxCacheTimeMs: number,
headers?: Record<string, string>
): Promise<object> {
): Promise<T> {
const result = await Utils.downloadJsonCachedAdvanced(url, maxCacheTimeMs, headers)
if (result["content"]) {
return result["content"]
@ -1023,11 +1023,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
throw result["error"]
}
public static async downloadJsonCachedAdvanced(
public static async downloadJsonCachedAdvanced<T = object | []>(
url: string,
maxCacheTimeMs: number,
headers?: Record<string, string>
): Promise<{ content: object | [] } | { error: string; url: string; statuscode?: number }> {
): Promise<{ content: T } | { error: string; url: string; statuscode?: number }> {
const cached = Utils._download_cache.get(url)
if (cached !== undefined) {
if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) {
@ -1042,10 +1042,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
Utils._download_cache.set(url, { promise, timestamp: new Date().getTime() })
return await promise
}
public static async downloadJson(
public static async downloadJson<T = object | []>(
url: string,
headers?: Record<string, string>
): Promise<object | []>
): Promise<T>
public static async downloadJson<T>(
url: string,
headers?: Record<string, string>

View file

@ -0,0 +1,19 @@
export default class MarkdownUtils {
public static table(header: string[], contents: string[][]){
let result = ""
result += "\n\n| "+header.join(" | ") + " |\n"
result += header.map(() => "-----").join("|") + " |\n"
for (const line of contents) {
if(!line){
continue
}
result += "| " + line.map(x => x ?? "").join(" | ") + " |\n"
}
result += "\n\n"
return result
}
}

View file

@ -445,7 +445,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -459,7 +459,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -672,7 +672,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -763,7 +763,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -776,7 +777,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -1225,7 +1226,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -1316,7 +1317,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -1329,7 +1331,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -2373,7 +2375,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -2387,7 +2389,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -2605,7 +2607,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -2699,7 +2701,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -2712,7 +2715,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -3074,7 +3077,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -3088,7 +3091,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -3306,7 +3309,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -3400,7 +3403,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -3413,7 +3417,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -3753,7 +3757,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -3767,7 +3771,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -3985,7 +3989,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -4079,7 +4083,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -4092,7 +4097,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -4434,7 +4439,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -4448,7 +4453,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -4666,7 +4671,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -4760,7 +4765,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -4773,7 +4779,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -5116,7 +5122,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -5130,7 +5136,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -5348,7 +5354,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -5442,7 +5448,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -5455,7 +5462,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -5803,7 +5810,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -5817,7 +5824,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -6035,7 +6042,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -6129,7 +6136,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -6142,7 +6150,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -6485,7 +6493,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -6499,7 +6507,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -6717,7 +6725,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -6811,7 +6819,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -6824,7 +6833,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -7167,7 +7176,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -7181,7 +7190,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -7399,7 +7408,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -7493,7 +7502,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -7506,7 +7516,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -7856,7 +7866,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -7870,7 +7880,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -8092,7 +8102,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -8186,7 +8196,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -8199,7 +8210,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -8550,7 +8561,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -8564,7 +8575,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -8786,7 +8797,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -8880,7 +8891,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -8893,7 +8905,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -9965,7 +9977,7 @@
"group": "presets"
},
"type": "array",
"description": "<div class='flex'>\n <div>\nPresets for this layer.\nA preset consists of one or more attributes (tags), a title and optionally a description and optionally example images.\nWhen the contributor wishes to add a point to OpenStreetMap, they'll:\n1. Press the 'add new point'-button\n2. Choose a preset from the list of all presets\n3. Confirm the choice. In this step, the `description` (if set) and `exampleImages` (if given) will be shown\n4. Confirm the location\n5. A new point will be created with the attributes that were defined in the preset\nIf no presets are defined, the button which invites to add a new preset will not be shown.\n</div>\n<a class='block m-2 min-w-64' href='./Docs/Screenshots/AddNewItemScreencast.webm' target='_blank'> <video controls autoplay muted src='./Docs/Screenshots/AddNewItemScreencast.webm' class='w-64'/></a>\n</div>"
"description": "<div class='flex'>\n <div>\nPresets for this layer.\nA preset consists of one or more attributes (tags), a title and optionally a description and optionally example images.\nWhen the contributor wishes to add a point to OpenStreetMap, they'll:\n1. Press the 'add new point'-button\n2. Choose a preset from the list of all presets\n3. Confirm the choice. In this step, the `description` (if set) and `exampleImages` (if given) will be shown\n4. Confirm the location\n5. A new point will be created with the attributes that were defined in the preset\nIf no presets are defined, the button which invites to add a new preset will not be shown.\n</div>\n<video controls autoplay muted src='./Docs/Screenshots/AddNewItemScreencast.webm' class='w-64'/>\n</div>"
},
{
"path": [
@ -10724,7 +10736,7 @@
"type": "string"
},
"placeholder": {
"description": "question: What placeholder text should be shown in the input-element if there is no input?\nA (translated) text that is shown (as gray text) within the textfield\ntype: translation\ngroup: expert",
"description": "question: What placeholder text should be shown in the input-element if there is no input?\nA (translated) text that is shown (as gray text) within the textfield\ntype: translation\ngroup: expert\nifunset: No specific placeholder is set, show the type of the textfield",
"anyOf": [
{
"$ref": "#/definitions/Record<string,string>"
@ -10872,7 +10884,7 @@
]
},
"condition": {
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or ask the question if unkown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"description": "question: When should this item be shown?\ntype: tag\nifunset: No specific condition set; always show this tagRendering or show this question if unknown\n\nOnly show this tagrendering (or ask the question) if the selected object also matches the tags specified as `condition`.\n\nThis is useful to ask a follow-up question.\nFor example, within toilets, asking _where_ the diaper changing table is is only useful _if_ there is one.\nThis can be done by adding `\"condition\": \"changing_table=yes\"`\n\nA full example would be:\n```json\n {\n \"question\": \"Where is the changing table located?\",\n \"render\": \"The changing table is located at {changing_table:location}\",\n \"condition\": \"changing_table=yes\",\n \"freeform\": {\n \"key\": \"changing_table:location\",\n \"inline\": true\n },\n \"mappings\": [\n {\n \"then\": \"The changing table is in the toilet for women.\",\n \"if\": \"changing_table:location=female_toilet\"\n },\n {\n \"then\": \"The changing table is in the toilet for men.\",\n \"if\": \"changing_table:location=male_toilet\"\n },\n {\n \"if\": \"changing_table:location=wheelchair_toilet\",\n \"then\": \"The changing table is in the toilet for wheelchair users.\",\n },\n {\n \"if\": \"changing_table:location=dedicated_room\",\n \"then\": \"The changing table is in a dedicated room. \",\n }\n ],\n \"id\": \"toilet-changing_table:location\"\n },\n```",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -10886,7 +10898,7 @@
]
},
"metacondition": {
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\n\nIf set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"description": "question: When should this item be shown (including special conditions)?\ntype: tag\nifunset: No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown\n\nIf set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_",
"anyOf": [
{
"$ref": "#/definitions/{and:TagConfigJson[];}"
@ -11675,7 +11687,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -12020,7 +12033,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -12111,7 +12124,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -12124,7 +12138,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -12952,7 +12966,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -13313,7 +13328,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -13407,7 +13422,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -13420,7 +13436,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -14261,7 +14277,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -14622,7 +14639,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -14716,7 +14733,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -14729,7 +14747,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -15587,7 +15605,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -15964,7 +15983,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -16061,7 +16080,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -16074,7 +16094,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -16910,7 +16930,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -17271,7 +17292,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -17365,7 +17386,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -17378,7 +17400,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -18236,7 +18258,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -18613,7 +18636,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -18710,7 +18733,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -18723,7 +18747,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [
@ -19442,10 +19466,10 @@
"additionalProperties": false
},
{
"$ref": "#/definitions/Record<string,string|{quantity:string;denominations:string[];canonical?:string|undefined;}>"
"$ref": "#/definitions/Record<string,string|{quantity:string;denominations:string[];canonical?:string|undefined;inverted?:boolean|undefined;}>"
}
],
"description": "Either a list with [{\"key\": \"unitname\", \"key2\": {\"quantity\": \"unitname\", \"denominations\": [\"denom\", \"denom\"]}}]"
"description": "Either a list with [{\"key\": \"unitname\", \"key2\": {\"quantity\": \"unitname\", \"denominations\": [\"denom\", \"denom\"]}}]\nUse `\"inverted\": true` if the amount should be _divided_ by the denomination, e.g. for charge over time (`€5/day`)"
},
{
"path": [

File diff suppressed because it is too large Load diff

View file

@ -683,7 +683,8 @@
"hints": {
"typehint": "translation",
"group": "expert",
"question": "What placeholder text should be shown in the input-element if there is no input?"
"question": "What placeholder text should be shown in the input-element if there is no input?",
"ifunset": "No specific placeholder is set, show the type of the textfield"
},
"type": [
{
@ -1012,7 +1013,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -1100,7 +1101,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -1113,7 +1115,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [

View file

@ -115,7 +115,7 @@
"hints": {
"typehint": "tag",
"question": "When should this item be shown?",
"ifunset": "No specific condition set; always show this tagRendering or ask the question if unkown"
"ifunset": "No specific condition set; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -203,7 +203,8 @@
"required": false,
"hints": {
"typehint": "tag",
"question": "When should this item be shown (including special conditions)?"
"question": "When should this item be shown (including special conditions)?",
"ifunset": "No specific metacondition set which is evaluated against the <i>usersettings/application state</i>; always show this tagRendering or show this question if unknown"
},
"type": [
{
@ -216,7 +217,7 @@
"type": "string"
}
],
"description": "If set, this tag will be evaluated agains the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options.\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
"description": "If set, this tag will be evaluated against the _usersettings/application state_ table.\nEnable 'show debug info' in user settings to see available options (at the settings-tab).\nNote that values with an underscore depicts _application state_ (including metainfo about the user) whereas values without an underscore depict _user settings_"
},
{
"path": [

View file

@ -1,4 +1,8 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement"
import Test from "./UI/Test.svelte"
import NameSuggestionIndex from "./Logic/Web/NameSuggestionIndex"
const nsi = NameSuggestionIndex.get()
const secondhandshops = nsi.getSuggestionsFor("brands/shop/second_hand", ["be"])
console.log(secondhandshops)
new SvelteUIElement(Test).AttachTo("maindiv")