diff --git a/src/Logic/Tags/TagUtils.ts b/src/Logic/Tags/TagUtils.ts index 55b142506..0a81a27a6 100644 --- a/src/Logic/Tags/TagUtils.ts +++ b/src/Logic/Tags/TagUtils.ts @@ -126,23 +126,7 @@ export class TagUtils { "\n" + "An assigning tag _cannot_ be used to query OpenStreetMap/Overpass.\n" + "\n" + - "If using a key or variable which might not be defined, add a condition in the mapping to hide the option. This is\n" + - "because, if `some_other_key` is not defined, one might actually upload the literal text `key={some_other_key}` to OSM -\n" + - "which we do not want.\n" + - "\n" + - "To mitigate this, use:\n" + - "\n" + - "```json\n" + - "{\n" + - ' "mappings": [\n' + - " {\n" + - ' "if":"key:={some_other_key}",\n' + - ' "then": "...",\n' + - ' "hideInAnswer": "some_other_key="\n' + - " }\n" + - " ]\n" + - "}\n" + - "```\n" + + "It is possible to assign a default value with `key_to_assing:={some_other_key??fallback_value}`. If `some_other_key` is not present, then the literal value `fallback_value` will be used instead." + "\n" + "One can use `key!:=prefix-{other_key}-postfix` as well, to match if `key` is _not_ the same\n" + "as `prefix-{other_key}-postfix` (with `other_key` substituted by the value)", @@ -263,8 +247,8 @@ export class TagUtils { return tags } - static SplitKeys(tagsFilters: UploadableTag[]): Record { - return this.SplitKeysRegex(tagsFilters, false) + static SplitKeys(tagsFilters: UploadableTag[], currentProperties: Tags): Record { + return this.SplitKeysRegex(tagsFilters, false, currentProperties) } /*** @@ -272,24 +256,31 @@ export class TagUtils { * * TagUtils.SplitKeysRegex([new Tag("isced:level", "bachelor; master")], true) // => {"isced:level": ["bachelor","master"]} */ - static SplitKeysRegex(tagsFilters: UploadableTag[], allowRegex: false): Record + static SplitKeysRegex(tagsFilters: ReadonlyArray, allowRegex: false, + currentProperties: Tags): Record static SplitKeysRegex( - tagsFilters: UploadableTag[], - allowRegex: boolean + tagsFilters: ReadonlyArray, + allowRegex: boolean, + currentProperties: Tags ): Record static SplitKeysRegex( - tagsFilters: UploadableTag[], - allowRegex: boolean + tagsFiltersIn: ReadonlyArray, + allowRegex: boolean, + currentProperties: Tags ): Record { const keyValues: Record = {} - tagsFilters = [...tagsFilters] // copy all, use as queue + const tagsFilters = [...tagsFiltersIn] // copy all, use as queue while (tagsFilters.length > 0) { - const tagsFilter = tagsFilters.shift() + let tagsFilter = tagsFilters.shift() if (tagsFilter === undefined) { continue } + if (tagsFilter instanceof SubstitutingTag) { + tagsFilter = (tagsFilter).asTag(currentProperties) + } + if (tagsFilter instanceof And) { tagsFilters.push(...(tagsFilter.and)) continue @@ -360,12 +351,12 @@ export class TagUtils { * TagUtils.FlattenMultiAnswer(([new Tag("x","y"), new Tag("a","b")])) // => [new Tag("x","y"), new Tag("a","b")] * TagUtils.FlattenMultiAnswer(([new Tag("x","")])) // => [new Tag("x","")] */ - static FlattenMultiAnswer(tagsFilters: UploadableTag[]): UploadableTag[] { + static FlattenMultiAnswer(tagsFilters: UploadableTag[], currentProperties: Tags): UploadableTag[] { if (tagsFilters === undefined) { return [] } - const keyValues = TagUtils.SplitKeys(tagsFilters) + const keyValues = TagUtils.SplitKeys(tagsFilters, currentProperties) const and: UploadableTag[] = [] for (const key in keyValues) { const values = Lists.dedup(keyValues[key]).filter((v) => v !== "") @@ -387,7 +378,7 @@ export class TagUtils { * TagUtils.MatchesMultiAnswer(new Tag("isced:level","master"), {"isced:level":"bachelor; master"}) // => true */ static MatchesMultiAnswer(tag: UploadableTag, properties: Tags): boolean { - const splitted = TagUtils.SplitKeysRegex([tag], true) + const splitted = TagUtils.SplitKeysRegex([tag], true, properties) for (const splitKey in splitted) { const neededValues = splitted[splitKey] if (properties[splitKey] === undefined) { diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index 29b342ed2..e188cdde7 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -897,7 +897,7 @@ export default class TagRenderingConfig { ]) ) } - const and = TagUtils.FlattenMultiAnswer([...selectedMappings, ...unselectedMappings]) + const and = TagUtils.FlattenMultiAnswer([...selectedMappings, ...unselectedMappings], currentProperties) if (and.length === 0) { return undefined } diff --git a/src/Utils.ts b/src/Utils.ts index 123818e2c..7ddf45d7c 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -300,6 +300,10 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * Utils.SubstituteKeys("abc{def}ghi", {def: '{XYZ}'}) // => "abc{XYZ}ghi" * Utils.SubstituteKeys("abc\n\n{def}ghi", {def: '{XYZ}'}) // => "abc\n\n{XYZ}ghi" * + * // Should support a default value + * Utils.SubstituteKeys("abc{def??XXX}ghi", {def: 'XYZ'}) // => "abcXYZghi" + * Utils.SubstituteKeys("abc{def??XXX}ghi", {randomKey: 'XYZ'}) // => "abcXXXghi" + * * @param txt * @param tags * @param useLang @@ -322,8 +326,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } let result = "" while (match) { - const [_, normal, key, leftover] = match - let v = tags?.[key] + const [_, normal, keyFallback, leftover] = match + let key = keyFallback + let fallback = "" + if (keyFallback.indexOf("??") >= 0) { + [key, fallback] = keyFallback.split("??") + } + let v = tags?.[key] ?? fallback if (v !== undefined && v !== null) { if (v["toISOString"] != undefined) { // This is a date, probably the timestamp of the object