Refactoring: split 'Utils' into multiple files; fix some stray uppercase-method names

This commit is contained in:
Pieter Vander Vennet 2025-08-01 04:02:09 +02:00
parent 81be4db044
commit 3ec89826e4
97 changed files with 884 additions and 921 deletions

View file

@ -9,6 +9,7 @@ import { Utils } from "../Utils"
import { TagUtils } from "../Logic/Tags/TagUtils"
import { And } from "../Logic/Tags/And"
import { GlobalFilter } from "./GlobalFilter"
import { Lists } from "../Utils/Lists"
export default class FilteredLayer {
/**
@ -287,7 +288,7 @@ export default class FilteredLayer {
}
needed.push(filter.options[state.data].osmTags)
}
needed = Utils.NoNull(needed)
needed = Lists.noNull(needed)
if (needed.length == 0) {
return undefined
}

View file

@ -11,9 +11,10 @@ import { ThemeConfigJson } from "../../src/Models/ThemeConfig/Json/ThemeConfigJs
import SpecialVisualizations from "../../src/UI/SpecialVisualizations"
import ValidationUtils from "../../src/Models/ThemeConfig/Conversion/ValidationUtils"
import {
QuestionableTagRenderingConfigJson
QuestionableTagRenderingConfigJson,
} from "../../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import { LayerConfigJson } from "../../src/Models/ThemeConfig/Json/LayerConfigJson"
import { Lists } from "../Utils/Lists"
export interface ServerSourceInfo {
url: string
@ -81,7 +82,7 @@ export class SourceOverview {
const geojsonSources: string[] = layout?.layers?.map((l) => l.source?.geojsonSource) ?? []
return Utils.NoNull(apiUrls.concat(...geojsonSources)).filter((item) => {
return Lists.noNull(apiUrls.concat(...geojsonSources)).filter((item) => {
if (typeof item === "string") {
return true
}
@ -117,7 +118,7 @@ export class SourceOverview {
"Background layer source or supporting sources for " + f.properties.id,
trigger: ["specific_feature"],
category: "maplayer",
moreInfo: Utils.NoEmpty([
moreInfo: Lists.noEmpty([
"https://github.com/osmlab/editor-layer-index",
f.properties?.attribution?.url,
]),

View file

@ -7,6 +7,7 @@ import {
QuestionableTagRenderingConfigJson,
} from "../Json/QuestionableTagRenderingConfigJson"
import { Utils } from "../../../Utils"
import { ConversionContext } from "./ConversionContext"
export default class AddPrefixToTagRenderingConfig extends DesugaringStep<QuestionableTagRenderingConfigJson> {
private readonly _prefix: string
@ -142,7 +143,8 @@ export default class AddPrefixToTagRenderingConfig extends DesugaringStep<Questi
}
public convert(
json: Readonly<QuestionableTagRenderingConfigJson>
json: Readonly<QuestionableTagRenderingConfigJson>,
context: ConversionContext
): QuestionableTagRenderingConfigJson {
let freeform = json.freeform
if (freeform) {

View file

@ -1,7 +1,7 @@
import { LayerConfigJson } from "../Json/LayerConfigJson"
import { Utils } from "../../../Utils"
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
import { ConversionContext } from "./ConversionContext"
import { Lists } from "../../../Utils/Lists"
export interface DesugaringContext {
tagRenderings: Map<string, QuestionableTagRenderingConfigJson>
@ -219,7 +219,7 @@ export class Concat<X, T> extends Conversion<X[], T[]> {
return <undefined | null>values
}
const vals: T[][] = new Each(this._step).convert(values, context.inOperation("concat"))
return [].concat(...vals)
return vals.flatMap(l => l)
}
}
@ -264,7 +264,6 @@ export class Cached<TIn, TOut> extends Conversion<TIn, TOut> {
}
export class Fuse<T> extends DesugaringStep<T> {
protected debug = false
private readonly steps: DesugaringStep<T>[]
constructor(doc: string, ...steps: DesugaringStep<T>[]) {
@ -274,18 +273,12 @@ export class Fuse<T> extends DesugaringStep<T> {
"This fused pipeline of the following steps: " +
steps.map((s) => s.name).join(", ")
)
this.steps = Utils.NoNull(steps)
this.steps = Lists.noNull(steps)
}
public enableDebugging(): Fuse<T> {
this.debug = true
return this
}
convert(json: T, context: ConversionContext): T {
const timings = []
for (let i = 0; i < this.steps.length; i++) {
const start = new Date()
const step = this.steps[i]
try {
const r = step.convert(json, context.inOperation(step.name))
@ -297,14 +290,6 @@ export class Fuse<T> extends DesugaringStep<T> {
console.error("Step " + step.name + " failed due to ", e, e.stack)
throw e
}
if (this.debug) {
const stop = new Date()
const timeNeededMs = stop.getTime() - start.getTime()
timings.push(timeNeededMs)
}
}
if (this.debug) {
console.log("Time needed,", timings.join(", "))
}
return json
}

View file

@ -1,9 +1,9 @@
import { DesugaringStep } from "./Conversion"
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { ConversionContext } from "./ConversionContext"
import { Utils } from "../../../Utils"
import Translations from "../../../UI/i18n/Translations"
import { DoesImageExist } from "./Validation"
import { Lists } from "../../../Utils/Lists"
export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJson> {
private readonly _doesImageExist: DoesImageExist
@ -45,7 +45,7 @@ export class DetectMappingsWithImages extends DesugaringStep<TagRenderingConfigJ
for (let i = 0; i < json.mappings.length; i++) {
const mapping = json.mappings[i]
const ignore = mapping["#"]?.indexOf(ignoreToken) >= 0
const images = Utils.Dedup(Translations.T(mapping.then)?.ExtractImages() ?? [])
const images = Lists.dedup(Translations.T(mapping.then)?.ExtractImages() ?? [])
const ctx = context.enters("mappings", i)
if (images.length > 0) {
if (!ignore) {

View file

@ -14,6 +14,7 @@ import Translations from "../../../UI/i18n/Translations"
import { FlatTag, OptimizedTag, TagsFilterClosed } from "../../../Logic/Tags/TagTypes"
import { TagsFilter } from "../../../Logic/Tags/TagsFilter"
import { Translation } from "../../../UI/i18n/Translation"
import { Lists } from "../../../Utils/Lists"
export class PruneFilters extends DesugaringStep<LayerConfigJson> {
constructor() {
@ -107,9 +108,7 @@ export class PruneFilters extends DesugaringStep<LayerConfigJson> {
const sourceTags = TagUtils.Tag(json.source["osmTags"])
return {
...json,
filter: Utils.NoNull(
json.filter?.map((obj) => this.prune(sourceTags, <FilterConfigJson>obj, context))
),
filter: Lists.noNull(json.filter?.map((obj) => this.prune(sourceTags, <FilterConfigJson>obj, context))),
}
}
}

View file

@ -8,6 +8,7 @@ import { Utils } from "../../../Utils"
import { AddContextToTranslations } from "./AddContextToTranslations"
import AddPrefixToTagRenderingConfig from "./AddPrefixToTagRenderingConfig"
import { Translatable } from "../Json/Translatable"
import { Lists } from "../../../Utils/Lists"
export class ExpandTagRendering extends Conversion<
| string
@ -41,8 +42,7 @@ export class ExpandTagRendering extends Conversion<
) {
super(
"ExpandTagRendering",
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question and reusing the builtins",
[]
"Converts a tagRenderingSpec into the full tagRendering, e.g. by substituting the tagRendering by the shared-question and reusing the builtins"
)
this._state = state
this._self = self
@ -387,7 +387,7 @@ export class ExpandTagRendering extends Conversion<
if (layer === undefined) {
const candidates = Utils.sortedByLevenshteinDistance(
layerName,
Utils.NoNull(Array.from(state.sharedLayers.keys()))
Lists.noNull(Array.from(state.sharedLayers.keys()))
)
if (candidates.length === 0) {
ctx.err(
@ -413,7 +413,7 @@ export class ExpandTagRendering extends Conversion<
// We are dealing with a looping import, no error is necessary
continue
}
candidates = Utils.NoNull(layer.tagRenderings.map((tr) => tr["id"])).map(
candidates = Lists.noNull(layer.tagRenderings.map((tr) => tr["id"])).map(
(id) => layerName + "." + id
)
}

View file

@ -7,6 +7,7 @@ import Translations from "../../../UI/i18n/Translations"
import { parse as parse_html } from "node-html-parser"
import { ConversionContext } from "./ConversionContext"
import { Lists } from "../../../Utils/Lists"
export class ExtractImages extends Conversion<
ThemeConfigJson,
@ -243,16 +244,12 @@ export class ExtractImages extends Conversion<
}
// Split "circle:white;./assets/layers/.../something.svg" into ["circle", "./assets/layers/.../something.svg"]
const allPaths = Utils.NoNull(
Utils.NoEmpty(
foundImage.path?.split(";")?.map((part) => {
if (part.startsWith("http")) {
return part
}
return part.split(":")[0]
})
)
)
const allPaths = Lists.noNull(Lists.noEmpty(foundImage.path?.split(";")?.map((part) => {
if (part.startsWith("http")) {
return part
}
return part.split(":")[0]
})))
for (const path of allPaths) {
cleanedImages.push({
path,

View file

@ -1,10 +1,10 @@
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { Utils } from "../../../Utils"
import LineRenderingConfigJson from "../Json/LineRenderingConfigJson"
import { LayerConfigJson } from "../Json/LayerConfigJson"
import { DesugaringStep, Each, Fuse, On } from "./Conversion"
import PointRenderingConfigJson from "../Json/PointRenderingConfigJson"
import { ConversionContext } from "./ConversionContext"
import { Lists } from "../../../Utils/Lists"
export class UpdateLegacyLayer extends DesugaringStep<
LayerConfigJson | string | { builtin; override }
@ -190,7 +190,7 @@ export class UpdateLegacyLayer extends DesugaringStep<
) {
iconConfig = iconConfig.render
}
const icon = Utils.NoEmpty(iconConfig.split(";"))
const icon = Lists.noEmpty(iconConfig.split(";"))
pr.marker = icon.map((i) => {
if (i.startsWith("http")) {
return { icon: i }
@ -281,7 +281,7 @@ class UpdateLegacyTheme extends DesugaringStep<ThemeConfigJson> {
}
}
oldThemeConfig.layers = Utils.NoNull(oldThemeConfig.layers)
oldThemeConfig.layers = Lists.noNull(oldThemeConfig.layers)
delete oldThemeConfig["language"]
delete oldThemeConfig["version"]
delete oldThemeConfig["clustering"]

View file

@ -1,18 +1,6 @@
import {
Concat,
DesugaringContext,
DesugaringStep,
Each,
FirstOf,
Fuse,
On,
SetDefault,
} from "./Conversion"
import { Concat, DesugaringContext, DesugaringStep, Each, FirstOf, Fuse, On, SetDefault } from "./Conversion"
import { LayerConfigJson } from "../Json/LayerConfigJson"
import {
MinimalTagRenderingConfigJson,
TagRenderingConfigJson,
} from "../Json/TagRenderingConfigJson"
import { MinimalTagRenderingConfigJson, TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { Utils } from "../../../Utils"
import RewritableConfigJson from "../Json/RewritableConfigJson"
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
@ -33,6 +21,7 @@ import { TagUtils } from "../../../Logic/Tags/TagUtils"
import { ExpandFilter, PruneFilters } from "./ExpandFilter"
import { ExpandTagRendering } from "./ExpandTagRendering"
import layerconfig from "../../../assets/schemas/layerconfigmeta.json"
import { Lists } from "../../../Utils/Lists"
class AddFiltersFromTagRenderings extends DesugaringStep<LayerConfigJson> {
constructor() {
@ -360,7 +349,7 @@ export class AddEditingElements extends DesugaringStep<LayerConfigJson> {
const addByDefault = this.builtinQuestions.filter((tr) => tr.labels?.indexOf(key) >= 0)
const ids = new Set(addByDefault.map((tr) => tr.id))
const idsInOrder = this._desugaring.tagRenderingOrder?.filter((id) => ids.has(id)) ?? []
return Utils.NoNull(idsInOrder.map((id) => this._desugaring.tagRenderings.get(id)))
return Lists.noNull(idsInOrder.map((id) => this._desugaring.tagRenderings.get(id)))
}
convert(json: LayerConfigJson, _: ConversionContext): LayerConfigJson {
@ -771,11 +760,11 @@ class ExpandIconBadges extends DesugaringStep<PointRenderingConfigJson> {
const condition = tr.condition
for (const trElement of tr.mappings) {
const showIf = TagUtils.optimzeJson({
and: Utils.NoNull([
condition,
{
or: Utils.NoNull([trElement.alsoShowIf, trElement.if]),
},
and: Lists.noNull([
condition,
{
or: Lists.noNull([trElement.alsoShowIf, trElement.if]),
},
]),
})
if (showIf === true) {
@ -984,14 +973,12 @@ export class AutoTitleIcon extends DesugaringStep<LayerConfigJson> {
const allAutoIndex = json.titleIcons.indexOf(<any>"auto:*")
if (allAutoIndex >= 0) {
const generated = Utils.NoNull(
json.tagRenderings.map((tr) => {
if (typeof tr === "string") {
return undefined
}
return this.createTitleIconsBasedOn(<any>tr)
})
)
const generated = Lists.noNull(json.tagRenderings.map((tr) => {
if (typeof tr === "string") {
return undefined
}
return this.createTitleIconsBasedOn(<any>tr)
}))
json.titleIcons.splice(allAutoIndex, 1, ...generated)
return json
}
@ -1113,9 +1100,7 @@ export class OrderTagRendering extends DesugaringStep<TagRenderingConfigJson | s
}
export class OrderLayer extends DesugaringStep<string | LayerConfigJson> {
private static readonly layerAttributesOrder: ReadonlyArray<string> = Utils.Dedup(
(<ConfigMeta[]>layerconfig).filter((c) => c.path.length === 1).map((c) => c.path[0])
)
private static readonly layerAttributesOrder: ReadonlyArray<string> = Lists.dedup((<ConfigMeta[]>layerconfig).filter((c) => c.path.length === 1).map((c) => c.path[0]))
constructor() {
super("OrderLayer", "Reorders a tagRendering to the default order")
@ -1151,7 +1136,8 @@ export class PrepareLayer extends Fuse<LayerConfigJson> {
"Fully prepares and expands a layer for the LayerConfig.",
new DeriveSource(),
new On("tagRenderings", new Each(new RewriteSpecial())),
new On("tagRenderings", new Concat(new ExpandRewrite()).andThenF(Utils.Flatten)),
new On("tagRenderings", new Concat(new ExpandRewrite())
.andThenF(Utils.Flatten)),
new On(
"tagRenderings",
(layer) =>

View file

@ -1,14 +1,4 @@
import {
Concat,
Conversion,
DesugaringContext,
DesugaringStep,
Each,
Fuse,
On,
Pass,
SetDefault,
} from "./Conversion"
import { Concat, Conversion, DesugaringContext, DesugaringStep, Each, Fuse, On, Pass, SetDefault } from "./Conversion"
import { ThemeConfigJson } from "../Json/ThemeConfigJson"
import { OrderLayer, PrepareLayer, RewriteSpecial } from "./PrepareLayer"
import { LayerConfigJson } from "../Json/LayerConfigJson"
@ -22,6 +12,7 @@ import ValidationUtils from "./ValidationUtils"
import { ConversionContext } from "./ConversionContext"
import { ConfigMeta } from "../../../UI/Studio/configMeta"
import themeconfig from "../../../assets/schemas/layoutconfigmeta.json"
import { Lists } from "../../../Utils/Lists"
class SubstituteLayer extends Conversion<string | LayerConfigJson, LayerConfigJson[]> {
private readonly _state: DesugaringContext
@ -198,7 +189,7 @@ export class AddDefaultLayers extends DesugaringStep<ThemeConfigJson> {
convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson {
const state = this._state
json.layers = Utils.NoNull([...(json.layers ?? [])])
json.layers = Lists.noNull([...(json.layers ?? [])])
const alreadyLoaded = new Set(json.layers.map((l) => l["id"]))
for (const layerName of Constants.added_by_default) {
@ -612,9 +603,7 @@ class PostvalidateTheme extends DesugaringStep<ThemeConfigJson> {
}
}
export class OrderTheme extends Fuse<ThemeConfigJson> {
private static readonly themeAttributesOrder: ReadonlyArray<string> = Utils.Dedup(
(<ConfigMeta[]>themeconfig).filter((c) => c.path.length === 1).map((c) => c.path[0])
)
private static readonly themeAttributesOrder: ReadonlyArray<string> = Lists.dedup((<ConfigMeta[]>themeconfig).filter((c) => c.path.length === 1).map((c) => c.path[0]))
constructor() {
super("Reorders the layer to the default order",

View file

@ -10,6 +10,7 @@ import { And } from "../../../Logic/Tags/And"
import { DoesImageExist, ValidateFilter, ValidatePointRendering } from "./Validation"
import { ValidateTagRenderings } from "./ValidateTagRenderings"
import { TagsFilterClosed } from "../../../Logic/Tags/TagTypes"
import { Lists } from "../../../Utils/Lists"
export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
private readonly _isBuiltin: boolean
@ -207,10 +208,8 @@ export class PrevalidateLayer extends DesugaringStep<LayerConfigJson> {
}
{
// duplicate ids in tagrenderings check
const duplicates = Utils.NoNull(
Utils.Duplicates(Utils.NoNull((json.tagRenderings ?? []).map((tr) => tr["id"])))
)
if (duplicates.length > 0) {
const duplicates = Lists.noNull(Utils.Duplicates(json.tagRenderings?.map((tr) => tr?.["id"])))
if (duplicates?.length > 0) {
// It is tempting to add an index to this warning; however, due to labels the indices here might be different from the index in the tagRendering list
context
.enter("tagRenderings")

View file

@ -1,8 +1,8 @@
import { TagRenderingConfigJson } from "../Json/TagRenderingConfigJson"
import { Utils } from "../../../Utils"
import SpecialVisualizations from "../../../UI/SpecialVisualizations"
import { RenderingSpecification, SpecialVisualization } from "../../../UI/SpecialVisualization"
import { QuestionableTagRenderingConfigJson } from "../Json/QuestionableTagRenderingConfigJson"
import { Lists } from "../../../Utils/Lists"
export default class ValidationUtils {
public static getAllSpecialVisualisations(
@ -51,9 +51,9 @@ export default class ValidationUtils {
JSON.stringify(renderingConfig.mappings)
)
}
const translations: any[] = Utils.NoNull([
renderingConfig.render,
...(renderingConfig.mappings ?? []).map((m) => m.then),
const translations: any[] = Lists.noNull([
renderingConfig.render,
...(renderingConfig.mappings ?? []).map((m) => m.then),
])
const all: RenderingSpecification[] = []
for (let translation of translations) {

View file

@ -10,6 +10,7 @@ import { Utils } from "../../Utils"
import { RegexTag } from "../../Logic/Tags/RegexTag"
import MarkdownUtils from "../../Utils/MarkdownUtils"
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
import { Lists } from "../../Utils/Lists"
export type FilterConfigOption = {
question: Translation
@ -225,17 +226,17 @@ export default class FilterConfig {
public GenerateDocs(): string {
const hasField = this.options.some((opt) => opt.fields?.length > 0)
return MarkdownUtils.table(
Utils.NoNull(["id", "question", "osmTags", hasField ? "fields" : undefined]),
Lists.noNull(["id", "question", "osmTags", hasField ? "fields" : undefined]),
this.options.map((opt, i) => {
const isDefault = this.options.length > 1 && (this.defaultSelection ?? 0) == i
return <string[]>(
Utils.NoNull([
this.id + "." + i,
isDefault ? `*${opt.question.txt}* (default)` : opt.question,
opt.osmTags?.asHumanString() ?? "",
opt.fields?.length > 0
? opt.fields.map((f) => f.name + " (" + f.type + ")").join(" ")
: undefined,
Lists.noNull([
this.id + "." + i,
isDefault ? `*${opt.question.txt}* (default)` : opt.question,
opt.osmTags?.asHumanString() ?? "",
opt.fields?.length > 0
? opt.fields.map((f) => f.name + " (" + f.type + ")").join(" ")
: undefined,
])
)
})

View file

@ -23,6 +23,7 @@ import MarkdownUtils from "../../Utils/MarkdownUtils"
import { And } from "../../Logic/Tags/And"
import OsmWiki from "../../Logic/Osm/OsmWiki"
import { UnitUtils } from "../UnitUtils"
import { Lists } from "../../Utils/Lists"
export default class LayerConfig extends WithContextLoader {
public static readonly syncSelectionAllowed = ["no", "local", "theme-only", "global"] as const
@ -226,7 +227,7 @@ export default class LayerConfig extends WithContextLoader {
}
if (json.lineRendering) {
this.lineRendering = Utils.NoNull(json.lineRendering).map(
this.lineRendering = Lists.noNull(json.lineRendering).map(
(r, i) => new LineRenderingConfig(r, `${context}[${i}]`)
)
} else {
@ -234,7 +235,7 @@ export default class LayerConfig extends WithContextLoader {
}
if (json.pointRendering) {
this.mapRendering = Utils.NoNull(json.pointRendering).map(
this.mapRendering = Lists.noNull(json.pointRendering).map(
(r, i) => new PointRenderingConfig(r, `${context}[${i}](${this.id})`)
)
} else {
@ -283,7 +284,7 @@ export default class LayerConfig extends WithContextLoader {
}
const missingIds =
Utils.NoNull(json.tagRenderings)?.filter(
Lists.noNull(json.tagRenderings)?.filter(
(tr) =>
typeof tr !== "string" &&
tr["builtin"] === undefined &&
@ -298,7 +299,7 @@ export default class LayerConfig extends WithContextLoader {
throw msg
}
this.tagRenderings = (Utils.NoNull(json.tagRenderings) ?? []).map(
this.tagRenderings = (Lists.noNull(json.tagRenderings) ?? []).map(
(tr, i) =>
new TagRenderingConfig(
<QuestionableTagRenderingConfigJson>tr,
@ -431,7 +432,7 @@ export default class LayerConfig extends WithContextLoader {
return [
`[${tr.id}](#${tr.id}) ${origDef}`,
Utils.NoNull([q, r, options]).join("<br/>"),
Lists.noNull([q, r, options]).join("<br/>"),
tr.labels.join(", "),
key,
]
@ -560,7 +561,7 @@ export default class LayerConfig extends WithContextLoader {
]
}
for (const revDep of Utils.Dedup(layerIsNeededBy?.get(this.id) ?? [])) {
for (const revDep of Lists.dedup(layerIsNeededBy?.get(this.id) ?? [])) {
extraProps.push(
["This layer is needed as dependency for layer", `[${revDep}](#${revDep})`].join(
" "
@ -568,32 +569,30 @@ export default class LayerConfig extends WithContextLoader {
)
}
const tableRows: string[][] = Utils.NoNull(
this.tagRenderings
.map((tr) => tr.FreeformValues())
.filter((values) => values !== undefined)
.filter((values) => values.key !== "id")
.map((values) => {
const embedded: string[] = values.values?.map((v) =>
OsmWiki.constructLinkMd(values.key, v)
) ?? ["_no preset options defined, or no values in them_"]
const statistics = `https://taghistory.raifer.tech/?#***/${encodeURIComponent(
values.key
)}/`
const tagInfo = `https://taginfo.openstreetmap.org/keys/${values.key}#values`
return [
[
`<a target="_blank" href='${tagInfo}'><img src='https://mapcomplete.org/assets/svg/search.svg' height='18px'></a>`,
`<a target="_blank" href='${statistics}'><img src='https://mapcomplete.org/assets/svg/statistics.svg' height='18px'></a>`,
OsmWiki.constructLinkMd(values.key),
].join(" "),
values.type === undefined
? "Multiple choice"
: `[${values.type}](../SpecialInputElements.md#${values.type})`,
embedded.join(" "),
]
})
)
const tableRows: string[][] = Lists.noNull(this.tagRenderings
.map((tr) => tr.FreeformValues())
.filter((values) => values !== undefined)
.filter((values) => values.key !== "id")
.map((values) => {
const embedded: string[] = values.values?.map((v) =>
OsmWiki.constructLinkMd(values.key, v)
) ?? ["_no preset options defined, or no values in them_"]
const statistics = `https://taghistory.raifer.tech/?#***/${encodeURIComponent(
values.key
)}/`
const tagInfo = `https://taginfo.openstreetmap.org/keys/${values.key}#values`
return [
[
`<a target="_blank" href='${tagInfo}'><img src='https://mapcomplete.org/assets/svg/search.svg' height='18px'></a>`,
`<a target="_blank" href='${statistics}'><img src='https://mapcomplete.org/assets/svg/statistics.svg' height='18px'></a>`,
OsmWiki.constructLinkMd(values.key),
].join(" "),
values.type === undefined
? "Multiple choice"
: `[${values.type}](../SpecialInputElements.md#${values.type})`,
embedded.join(" "),
]
}))
let quickOverview: string[] = []
if (tableRows.length > 0) {
@ -693,7 +692,7 @@ export default class LayerConfig extends WithContextLoader {
}
AllTagRenderings(): TagRenderingConfig[] {
return Utils.NoNull([...this.tagRenderings, ...this.titleIcons, this.title])
return Lists.noNull([...this.tagRenderings, ...this.titleIcons, this.title])
}
public isLeftRightSensitive(): boolean {

View file

@ -5,10 +5,7 @@ import { TagUtils } from "../../Logic/Tags/TagUtils"
import { And } from "../../Logic/Tags/And"
import { Utils } from "../../Utils"
import { Tag } from "../../Logic/Tags/Tag"
import {
MappingConfigJson,
QuestionableTagRenderingConfigJson,
} from "./Json/QuestionableTagRenderingConfigJson"
import { MappingConfigJson, QuestionableTagRenderingConfigJson } from "./Json/QuestionableTagRenderingConfigJson"
import Validators, { ValidatorType } from "../../UI/InputElement/Validators"
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
import { RegexTag } from "../../Logic/Tags/RegexTag"
@ -21,6 +18,7 @@ import { UploadableTag } from "../../Logic/Tags/TagTypes"
import LayerConfig from "./LayerConfig"
import ComparingTag from "../../Logic/Tags/ComparingTag"
import { Unit } from "../Unit"
import { Lists } from "../../Utils/Lists"
export interface Mapping {
readonly if: UploadableTag
@ -317,7 +315,7 @@ export default class TagRenderingConfig {
}
keys.push(...mapping.if.usedKeys())
}
keys = Utils.Dedup(keys)
keys = Lists.dedup(keys)
for (let i = 0; i < this.mappings.length; i++) {
const mapping = this.mappings[i]
if (mapping.hideInAnswer) {
@ -352,7 +350,7 @@ export default class TagRenderingConfig {
}
allKeys = allKeys.concat(mapping.if.usedKeys())
}
allKeys = Utils.Dedup(allKeys)
allKeys = Lists.dedup(allKeys)
if (allKeys.length > 1 && !allHaveIfNot) {
throw `${context}: A multi-answer is defined, which generates values over multiple keys. Please define ifnot-tags too on every mapping`
}
@ -541,20 +539,18 @@ export default class TagRenderingConfig {
if?: TagsFilter
then: TypedTranslation<Record<string, string>>
img?: string
}[] = Utils.NoNull(
(this.mappings ?? [])?.filter((mapping) => {
if (mapping.if === undefined) {
return true
}
if (TagUtils.MatchesMultiAnswer(mapping.if, tags)) {
return true
}
if (mapping.alsoShowIf?.matchesProperties(tags)) {
return true
}
return false
})
)
}[] = Lists.noNull((this.mappings ?? [])?.filter((mapping) => {
if (mapping.if === undefined) {
return true
}
if (TagUtils.MatchesMultiAnswer(mapping.if, tags)) {
return true
}
if (mapping.alsoShowIf?.matchesProperties(tags)) {
return true
}
return false
}))
if (freeformKeyDefined && tags[this.freeform.key] !== undefined) {
const usedFreeformValues = new Set<string>(
@ -659,9 +655,7 @@ export default class TagRenderingConfig {
const key = this.freeform?.key
const answerMappings = this.mappings?.filter((m) => m.hideInAnswer !== true)
if (key === undefined) {
const values: { k: string; v: string }[][] = Utils.NoNull(
answerMappings?.map((m) => m.if.asChange({})) ?? []
)
const values: { k: string; v: string }[][] = Lists.noNull(answerMappings?.map((m) => m.if.asChange({})) ?? [])
if (values.length === 0) {
return
}
@ -677,17 +671,13 @@ export default class TagRenderingConfig {
}
return {
key: commonKey,
values: Utils.NoNull(
values.map((arr) => arr.filter((item) => item.k === commonKey)[0]?.v)
),
values: Lists.noNull(values.map((arr) => arr.filter((item) => item.k === commonKey)[0]?.v)),
}
}
let values = Utils.NoNull(
answerMappings?.map(
(m) => m.if.asChange({}).filter((item) => item.k === key)[0]?.v
) ?? []
)
let values = Lists.noNull(answerMappings?.map(
(m) => m.if.asChange({}).filter((item) => item.k === key)[0]?.v
) ?? [])
if (values.length === undefined) {
values = undefined
}
@ -1027,18 +1017,18 @@ export default class TagRenderingConfig {
].join(" ")
}
return Utils.NoNull([
"### " + this.id,
this.description,
this.question !== undefined
? "The question is `" + this.question.txt + "`"
: "_This tagrendering has no question and is thus read-only_",
freeform,
mappings,
condition,
labels,
"",
reuse,
return Lists.noNull([
"### " + this.id,
this.description,
this.question !== undefined
? "The question is `" + this.question.txt + "`"
: "_This tagrendering has no question and is thus read-only_",
freeform,
mappings,
condition,
labels,
"",
reuse,
]).join("\n")
}
@ -1061,7 +1051,7 @@ export default class TagRenderingConfig {
tags.push(m.ifnot)
}
return Utils.NoNull(tags)
return Lists.noNull(tags)
}
/**

View file

@ -13,6 +13,7 @@ import { TagsFilter } from "../../Logic/Tags/TagsFilter"
import TagRenderingConfig from "./TagRenderingConfig"
import { TagUtils } from "../../Logic/Tags/TagUtils"
import { TagRenderingConfigJson } from "./Json/TagRenderingConfigJson"
import { Lists } from "../../Utils/Lists"
/**
* Minimal information about a theme
@ -381,7 +382,7 @@ export default class ThemeConfig implements ThemeInformation {
const usedImages = jsonNoFavourites._usedImages
usedImages.sort()
this.usedImages = Utils.Dedup(usedImages)
this.usedImages = Lists.dedup(usedImages)
return this.usedImages
}
}