forked from MapComplete/MapComplete
Refactoring: split 'Utils' into multiple files; fix some stray uppercase-method names
This commit is contained in:
parent
81be4db044
commit
3ec89826e4
97 changed files with 884 additions and 921 deletions
|
@ -5,6 +5,7 @@ import { LayerConfigJson } from "../../../src/Models/ThemeConfig/Json/LayerConfi
|
|||
import FilterConfigJson from "../../../src/Models/ThemeConfig/Json/FilterConfigJson"
|
||||
import RewritableConfigJson from "../../../src/Models/ThemeConfig/Json/RewritableConfigJson"
|
||||
import { TagRenderingConfigJson } from "../../../src/Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import { Lists } from "../../../src/Utils/Lists"
|
||||
|
||||
interface ChargingStandard {
|
||||
key: string,
|
||||
|
@ -27,36 +28,36 @@ function colonSplit(value: string): string[] {
|
|||
}
|
||||
|
||||
function loadCsv(file): ChargingStandard[] {
|
||||
const entries: string[] = Utils.NoNull(readFileSync(file, "utf8").split("\n").map(str => str.trim()))
|
||||
const entries: string[] = Lists.noNull(readFileSync(file, "utf8").split("\n").map(str => str.trim()))
|
||||
const header = entries.shift().split(",")
|
||||
|
||||
return Utils.NoNull(entries.map(entry => {
|
||||
const values = entry.split(",").map(str => str.trim())
|
||||
if (values[0] === undefined || values[0] === "") {
|
||||
return undefined
|
||||
return Lists.noNull(entries.map(entry => {
|
||||
const values = entry.split(",").map(str => str.trim())
|
||||
if (values[0] === undefined || values[0] === "") {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const v = {}
|
||||
const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList", "countryBlackList", "associatedVehicleTypes", "neverAssociatedWith"]
|
||||
const descriptionTranslations = new Map<string, string>()
|
||||
for (let j = 0; j < header.length; j++) {
|
||||
const key = header[j]
|
||||
if (key.startsWith("description")) {
|
||||
const language = key.substring("description:".length)
|
||||
descriptionTranslations.set(language, values[j])
|
||||
}
|
||||
|
||||
const v = {}
|
||||
const colonSeperated = ["commonVoltages", "commonOutputs", "commonCurrents", "countryWhiteList", "countryBlackList", "associatedVehicleTypes", "neverAssociatedWith"]
|
||||
const descriptionTranslations = new Map<string, string>()
|
||||
for (let j = 0; j < header.length; j++) {
|
||||
const key = header[j]
|
||||
if (key.startsWith("description")) {
|
||||
const language = key.substring("description:".length)
|
||||
descriptionTranslations.set(language, values[j])
|
||||
}
|
||||
|
||||
if (colonSeperated.indexOf(key) >= 0) {
|
||||
v[key] = colonSplit(values[j])
|
||||
} else {
|
||||
v[key] = values[j]
|
||||
}
|
||||
if (colonSeperated.indexOf(key) >= 0) {
|
||||
v[key] = colonSplit(values[j])
|
||||
} else {
|
||||
v[key] = values[j]
|
||||
}
|
||||
v["description"] = descriptionTranslations
|
||||
if (v["id"] === "") {
|
||||
v["id"] = v["key"]
|
||||
}
|
||||
return <any>v
|
||||
}
|
||||
v["description"] = descriptionTranslations
|
||||
if (v["id"] === "") {
|
||||
v["id"] = v["key"]
|
||||
}
|
||||
return <any>v
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -161,7 +162,7 @@ function run(file, protojson) {
|
|||
// We add a second time for any amount to trigger a visualisation; but this is not an answer option
|
||||
const no_ask_json = {
|
||||
if: {
|
||||
and: Utils.NoEmpty([`${e.key}~*`, `${e.key}!=1`, ...e.extraVisualisationCondition.split(";")]),
|
||||
and: Lists.noEmpty([`${e.key}~*`, `${e.key}!=1`, ...e.extraVisualisationCondition.split(";")]),
|
||||
},
|
||||
then: txt,
|
||||
hideInAnswer: true,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -943,9 +943,9 @@
|
|||
"ru": "Onroerend Erfgoed ID:"
|
||||
},
|
||||
"special": {
|
||||
"type": "link",
|
||||
"href": "https://id.erfgoed.net/erfgoedobjecten/{ref:OnroerendErfgoed}",
|
||||
"text": "{ref:OnroerendErfgoed}"
|
||||
"text": "{ref:OnroerendErfgoed}",
|
||||
"type": "link"
|
||||
}
|
||||
},
|
||||
"icon": "./assets/layers/tree/Onroerend_Erfgoed_logo_without_text.svg",
|
||||
|
|
|
@ -103,13 +103,13 @@ class StatsDownloader {
|
|||
let page = 1
|
||||
let allFeatures: ChangeSetData[] = []
|
||||
const endDay = new Date(year, month - 1 /* Zero-indexed: 0 = january*/, day + 1)
|
||||
const endDate = `${endDay.getFullYear()}-${Utils.TwoDigits(
|
||||
const endDate = `${endDay.getFullYear()}-${Utils.twoDigits(
|
||||
endDay.getMonth() + 1
|
||||
)}-${Utils.TwoDigits(endDay.getDate())}`
|
||||
)}-${Utils.twoDigits(endDay.getDate())}`
|
||||
let url = this.urlTemplate
|
||||
.replace(
|
||||
"{start_date}",
|
||||
year + "-" + Utils.TwoDigits(month) + "-" + Utils.TwoDigits(day)
|
||||
year + "-" + Utils.twoDigits(month) + "-" + Utils.twoDigits(day)
|
||||
)
|
||||
.replace("{end_date}", endDate)
|
||||
.replace("{page}", "" + page)
|
||||
|
@ -142,7 +142,7 @@ class StatsDownloader {
|
|||
}
|
||||
url = result.next
|
||||
}
|
||||
allFeatures = Utils.NoNull(allFeatures)
|
||||
allFeatures = Lists.noNull(allFeatures)
|
||||
allFeatures.forEach((f) => {
|
||||
f.properties = { ...f.properties, ...f.properties.metadata }
|
||||
if (f.properties.editor.toLowerCase().indexOf("android") >= 0) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import { existsSync, readFileSync, writeFileSync } from "fs"
|
|||
import WikidataUtils from "../src/Utils/WikidataUtils"
|
||||
import LanguageUtils from "../src/Utils/LanguageUtils"
|
||||
import Wikidata from "../src/Logic/Web/Wikidata"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
interface value<T> {
|
||||
value: T
|
||||
|
@ -196,7 +197,7 @@ async function main(wipeCache = false) {
|
|||
})
|
||||
|
||||
translatedForId["_meta"] = {
|
||||
countries: Utils.Dedup(languagesPerCountry[key]),
|
||||
countries: Lists.dedup(languagesPerCountry[key]),
|
||||
dir: value.directionality,
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import Validators from "../src/UI/InputElement/Validators"
|
|||
import { AllKnownLayouts } from "../src/Customizations/AllKnownLayouts"
|
||||
import { AllSharedLayers } from "../src/Customizations/AllSharedLayers"
|
||||
import Constants from "../src/Models/Constants"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
const metainfo = {
|
||||
type: "One of the inputValidator types",
|
||||
|
@ -371,7 +372,7 @@ function extractMeta(
|
|||
const fullPath = "./src/assets/schemas/" + path + ".json"
|
||||
writeFileSync(fullPath, JSON.stringify(paths, null, " "))
|
||||
console.log("Written meta to " + fullPath)
|
||||
return Utils.NoNull(paths.map((p) => validateMeta(p)))
|
||||
return Lists.noNull(paths.map((p) => validateMeta(p)))
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
|
|
@ -34,6 +34,7 @@ import { ImmutableStore } from "../src/Logic/UIEventSource"
|
|||
import * as unitUsage from "../Docs/Schemas/UnitConfigJson.schema.json"
|
||||
import { ThemeConfigJson } from "../src/Models/ThemeConfig/Json/ThemeConfigJson"
|
||||
import { ServerSourceInfo, SourceOverview } from "../src/Models/SourceOverview"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
/**
|
||||
* Converts a markdown-file into a .json file, which a walkthrough/slideshow element can use
|
||||
|
@ -562,7 +563,7 @@ export class GenerateDocs extends Script {
|
|||
)
|
||||
)
|
||||
const serverInfos = Utils.DedupOnId(serverInfosDupl, (item) => item.url)
|
||||
const titles = Utils.Dedup(Utils.NoEmpty(serverInfos.map((s) => s.category)))
|
||||
const titles = Lists.dedup(Lists.noEmpty(serverInfos.map((s) => s.category)))
|
||||
titles.sort()
|
||||
|
||||
function getHost(item: ServerSourceInfo) {
|
||||
|
@ -597,7 +598,7 @@ export class GenerateDocs extends Script {
|
|||
md.push(items.length + " items")
|
||||
md.push(categoryExplanation[title])
|
||||
|
||||
const hosts = Utils.Dedup(items.map(getHost))
|
||||
const hosts = Lists.dedup(items.map(getHost))
|
||||
hosts.sort()
|
||||
if (title === "maplayer") {
|
||||
md.push(MarkdownUtils.list(hosts))
|
||||
|
@ -636,7 +637,7 @@ export class GenerateDocs extends Script {
|
|||
return [
|
||||
item.url,
|
||||
identicalDescription ? "" : item.description,
|
||||
Utils.NoEmpty([
|
||||
Lists.noEmpty([
|
||||
item.openData ? "OpenData" : "",
|
||||
sourceAvailable,
|
||||
selfHostable,
|
||||
|
@ -720,9 +721,7 @@ export class GenerateDocs extends Script {
|
|||
MarkdownUtils.list(
|
||||
Constants.priviliged_layers.map((id) => "[" + id + "](#" + id + ")")
|
||||
),
|
||||
...Utils.NoNull(
|
||||
Constants.priviliged_layers.map((id) => AllSharedLayers.sharedLayers.get(id))
|
||||
).map((l) =>
|
||||
...Lists.noNull(Constants.priviliged_layers.map((id) => AllSharedLayers.sharedLayers.get(id))).map((l) =>
|
||||
l.generateDocumentation({
|
||||
usedInThemes: themesPerLayer.get(l.id),
|
||||
layerIsNeededBy: layerIsNeededBy,
|
||||
|
|
|
@ -13,6 +13,7 @@ import { TagUtils } from "../src/Logic/Tags/TagUtils"
|
|||
import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
|
||||
import * as questions from "../assets/layers/questions/questions.json"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
export class GenerateFavouritesLayer extends Script {
|
||||
private readonly layers: LayerConfigJson[] = []
|
||||
|
@ -155,7 +156,7 @@ export class GenerateFavouritesLayer extends Script {
|
|||
continue
|
||||
}
|
||||
newTr.condition = {
|
||||
and: Utils.NoNull([newTr.condition, layerConfig.source["osmTags"]]),
|
||||
and: Lists.noNull([newTr.condition, layerConfig.source["osmTags"]]),
|
||||
}
|
||||
generatedTagRenderings.push(newTr)
|
||||
blacklistedIds.add(newTr.id)
|
||||
|
|
|
@ -37,10 +37,8 @@ import { Translatable } from "../src/Models/ThemeConfig/Json/Translatable"
|
|||
import { ValidateThemeAndLayers } from "../src/Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
|
||||
import { ExtractImages } from "../src/Models/ThemeConfig/Conversion/FixImages"
|
||||
import { TagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import {
|
||||
LayerConfigDependencyGraph,
|
||||
LevelInfo,
|
||||
} from "../src/Models/ThemeConfig/LayerConfigDependencyGraph"
|
||||
import { LayerConfigDependencyGraph, LevelInfo } from "../src/Models/ThemeConfig/LayerConfigDependencyGraph"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
// This scripts scans 'src/assets/layers/*.json' for layer definition files and 'src/assets/themes/*.json' for theme definition files.
|
||||
// It spits out an overview of those to be used to load them
|
||||
|
@ -126,20 +124,18 @@ class AddIconSummary extends DesugaringStep<{ raw: LayerConfigJson; parsed: Laye
|
|||
pr.location.has("point")
|
||||
)
|
||||
const defaultTags = layerConfig.baseTags
|
||||
fixed["_layerIcon"] = Utils.NoNull(
|
||||
(pointRendering?.marker ?? []).map((i) => {
|
||||
const icon = i.icon?.GetRenderValue(defaultTags)?.txt
|
||||
if (!icon) {
|
||||
return undefined
|
||||
}
|
||||
const result = { icon }
|
||||
const c = i.color?.GetRenderValue(defaultTags)?.txt
|
||||
if (c) {
|
||||
result["color"] = c
|
||||
}
|
||||
return result
|
||||
})
|
||||
)
|
||||
fixed["_layerIcon"] = Lists.noNull((pointRendering?.marker ?? []).map((i) => {
|
||||
const icon = i.icon?.GetRenderValue(defaultTags)?.txt
|
||||
if (!icon) {
|
||||
return undefined
|
||||
}
|
||||
const result = { icon }
|
||||
const c = i.color?.GetRenderValue(defaultTags)?.txt
|
||||
if (c) {
|
||||
result["color"] = c
|
||||
}
|
||||
return result
|
||||
}))
|
||||
return { raw: fixed, parsed: layerConfig }
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +229,7 @@ class LayerBuilder extends Conversion<object, Map<string, LayerConfigJson>> {
|
|||
const origIds: ReadonlyArray<string> = [...ids]
|
||||
|
||||
const deps = this._dependencies
|
||||
const allDeps = Utils.Dedup([].concat(...ids.map((id) => deps.get(id))))
|
||||
const allDeps = Lists.dedup([].concat(...ids.map((id) => deps.get(id))))
|
||||
const depsRecord = Utils.asRecord(Array.from(deps.keys()), (k) =>
|
||||
deps.get(k).filter((dep) => ids.indexOf(dep) >= 0)
|
||||
)
|
||||
|
@ -864,7 +860,7 @@ class LayerOverviewUtils extends Script {
|
|||
)
|
||||
}
|
||||
if(printAssets){
|
||||
const images = Utils.Dedup(Array.from(sharedThemes.values()).flatMap(th => th._usedImages ?? [] ))
|
||||
const images = Lists.dedup(Array.from(sharedThemes.values()).flatMap(th => th._usedImages ?? []))
|
||||
writeFileSync("needed_assets.csv", images.join("\n"))
|
||||
console.log("Written needed_assets.csv")
|
||||
}
|
||||
|
@ -1278,11 +1274,9 @@ class LayerOverviewUtils extends Script {
|
|||
}
|
||||
}
|
||||
|
||||
const usedImages = Utils.Dedup(
|
||||
new ExtractImages(true, knownTagRenderings)
|
||||
.convertStrict(themeFile)
|
||||
.map((x) => x.path)
|
||||
)
|
||||
const usedImages = Lists.dedup(new ExtractImages(true, knownTagRenderings)
|
||||
.convertStrict(themeFile)
|
||||
.map((x) => x.path))
|
||||
usedImages.sort()
|
||||
|
||||
themeFile["_usedImages"] = usedImages
|
||||
|
|
|
@ -3,6 +3,7 @@ import SmallLicense from "../src/Models/smallLicense"
|
|||
import ScriptUtils from "./ScriptUtils"
|
||||
import Script from "./Script"
|
||||
import { Utils } from "../src/Utils"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
const prompt = require("prompt-sync")()
|
||||
|
||||
|
@ -297,9 +298,7 @@ export class GenerateLicenseInfo extends Script {
|
|||
sources: license.sources,
|
||||
}
|
||||
|
||||
cloned.license = Utils.Dedup(
|
||||
cloned.license.split(";").map((l) => this.toSPDXCompliantLicense(l))
|
||||
).join("; ")
|
||||
cloned.license = Lists.dedup(cloned.license.split(";").map((l) => this.toSPDXCompliantLicense(l))).join("; ")
|
||||
if (cloned.license === "CC0-1.0; TRIVIAL") {
|
||||
cloned.license = "TRIVIAL"
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import Script from "./Script"
|
|||
import NameSuggestionIndex from "../src/Logic/Web/NameSuggestionIndex"
|
||||
import TagInfo from "../src/Logic/Web/TagInfo"
|
||||
import { TagsFilter } from "../src/Logic/Tags/TagsFilter"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
class GenerateNsiStats extends Script {
|
||||
async createOptimizationFile(includeTags = true) {
|
||||
|
@ -139,9 +140,7 @@ class GenerateNsiStats extends Script {
|
|||
)
|
||||
}
|
||||
const nsi = await NameSuggestionIndex.singleton()
|
||||
const allBrandNames: string[] = Utils.Dedup(
|
||||
nsi.allPossible(<any>type).map((item) => item.tags[type])
|
||||
)
|
||||
const allBrandNames: string[] = Lists.dedup(nsi.allPossible(<any>type).map((item) => item.tags[type]))
|
||||
const batchSize = 50
|
||||
for (let i = 0; i < allBrandNames.length; i += batchSize) {
|
||||
console.warn(
|
||||
|
@ -152,7 +151,7 @@ class GenerateNsiStats extends Script {
|
|||
)
|
||||
let downloaded = 0
|
||||
await Promise.all(
|
||||
Utils.TimesT(batchSize, async (j) => {
|
||||
Utils.timesT(batchSize, async (j) => {
|
||||
const brand = allBrandNames[i + j]
|
||||
if (!allBrands[brand]) {
|
||||
allBrands[brand] = {}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { readFileSync, writeFileSync } from "fs"
|
|||
import ThemeConfig from "../src/Models/ThemeConfig/ThemeConfig"
|
||||
import LayerConfig from "../src/Models/ThemeConfig/LayerConfig"
|
||||
import { Utils } from "../src/Utils"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
/**
|
||||
* Generates all the files in "Docs/TagInfo". These are picked up by the taginfo project, showing a link to the mapcomplete theme if the key is used
|
||||
|
@ -269,7 +270,7 @@ function main() {
|
|||
}
|
||||
files.push(generateTagInfoEntry(layout))
|
||||
}
|
||||
generateProjectsOverview(Utils.NoNull(files))
|
||||
generateProjectsOverview(Lists.noNull(files))
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Utils } from "../src/Utils"
|
|||
import ScriptUtils from "./ScriptUtils"
|
||||
import Script from "./Script"
|
||||
import Constants from "../src/Models/Constants"
|
||||
import { Lists } from "../src/Utils/Lists"
|
||||
|
||||
const knownLanguages = ["en", "nl", "de", "fr", "es", "gl", "ca"]
|
||||
const ignoreTerms = ["searchTerms"]
|
||||
|
@ -172,7 +173,7 @@ class TranslationPart {
|
|||
languages.push(...(value as TranslationPart).knownLanguages())
|
||||
}
|
||||
}
|
||||
return Utils.Dedup(languages)
|
||||
return Lists.dedup(languages)
|
||||
}
|
||||
|
||||
toJson(neededLanguage?: string): string {
|
||||
|
@ -351,7 +352,7 @@ function transformTranslation(
|
|||
}
|
||||
|
||||
const values: string[] = []
|
||||
const spaces = Utils.Times((_) => " ", path.length + 1)
|
||||
const spaces = Utils.times((_) => " ", path.length + 1)
|
||||
|
||||
for (const key in obj) {
|
||||
if (key === "#") {
|
||||
|
@ -381,7 +382,7 @@ function transformTranslation(
|
|||
let expr = `new Translation(${JSON.stringify(value)}, "core:${path.join(".")}.${key}")`
|
||||
if (subParts !== null) {
|
||||
// convert '{to_substitute}' into 'to_substitute'
|
||||
const types = Utils.Dedup(subParts.map((tp) => tp.substring(1, tp.length - 1)))
|
||||
const types = Lists.dedup(subParts.map((tp) => tp.substring(1, tp.length - 1)))
|
||||
const invalid = types.filter(
|
||||
(part) => part.match(/^[a-z0-9A-Z_]+(\(.*\))?$/) == null
|
||||
)
|
||||
|
@ -764,7 +765,7 @@ class GenerateTranslations extends Script {
|
|||
"themes"
|
||||
)
|
||||
|
||||
const usedLanguages: string[] = Utils.Dedup(l1.concat(l2)).filter((v) => v !== "*")
|
||||
const usedLanguages: string[] = Lists.dedup(l1.concat(l2)).filter((v) => v !== "*")
|
||||
usedLanguages.sort()
|
||||
fs.writeFileSync(
|
||||
"./src/assets/used_languages.json",
|
||||
|
|
|
@ -128,7 +128,7 @@ class NsiLogos extends Script {
|
|||
}
|
||||
|
||||
const results = await Promise.all(
|
||||
Utils.TimesT(stepcount, (j) => j).map(async (j) => {
|
||||
Utils.timesT(stepcount, (j) => j).map(async (j) => {
|
||||
return await this.downloadLogo(items[i + j], type, basePath, alreadyDownloaded)
|
||||
})
|
||||
)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Script from "../Script"
|
||||
import { readFileSync, writeFileSync } from "fs"
|
||||
import { OsmId } from "../../src/Models/OsmFeature"
|
||||
import { Utils } from "../../src/Utils"
|
||||
import { Lists } from "../../src/Utils/Lists"
|
||||
|
||||
interface DiffItem {
|
||||
/**
|
||||
|
@ -34,7 +34,7 @@ export class DiffToCsv extends Script {
|
|||
JSON.parse(readFileSync(file, "utf8"))
|
||||
)
|
||||
const diffs = json.diffs
|
||||
const allKeys = Utils.Dedup(diffs.flatMap((item) => item.diffs.map((d) => d.key)))
|
||||
const allKeys = Lists.dedup(diffs.flatMap((item) => item.diffs.map((d) => d.key)))
|
||||
allKeys.sort()
|
||||
|
||||
const header = [
|
||||
|
|
|
@ -74,7 +74,7 @@ class VeloParkToGeojson extends Script {
|
|||
const batchSize = 50
|
||||
for (let i = 0; i < allVeloparkRaw.length; i += batchSize) {
|
||||
await Promise.all(
|
||||
Utils.TimesT(batchSize, (j) => j).map(async (j) => {
|
||||
Utils.timesT(batchSize, (j) => j).map(async (j) => {
|
||||
const f = allVeloparkRaw[i + j]
|
||||
if (!f) {
|
||||
return
|
||||
|
|
|
@ -3,6 +3,7 @@ import Locale from "../../UI/i18n/Locale"
|
|||
import { Utils } from "../../Utils"
|
||||
import { Feature } from "geojson"
|
||||
import { SpecialVisualizationState } from "../../UI/SpecialVisualization"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class TitleHandler {
|
||||
constructor(selectedElement: Store<Feature>, state: SpecialVisualizationState) {
|
||||
|
@ -22,7 +23,7 @@ export default class TitleHandler {
|
|||
if (layer.title === undefined) {
|
||||
return defaultTitle
|
||||
}
|
||||
const toRender = Utils.NoNull(layer?.title?.GetRenderValues(tags))
|
||||
const toRender = Lists.noNull(layer?.title?.GetRenderValues(tags))
|
||||
const titleUnsubbed = toRender[0]?.then?.textFor(lng)
|
||||
if (titleUnsubbed === undefined) {
|
||||
return defaultTitle
|
||||
|
|
|
@ -17,6 +17,7 @@ import { ThemeConfigJson } from "../Models/ThemeConfig/Json/ThemeConfigJson"
|
|||
import { ValidateThemeAndLayers } from "../Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
|
||||
import * as theme_overview from "../assets/generated/theme_overview.json"
|
||||
import * as favourite_layer from "../../assets/layers/favourite/favourite.json"
|
||||
import { Lists } from "../Utils/Lists"
|
||||
export default class DetermineTheme {
|
||||
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
|
||||
private static readonly loadCustomThemeParam = QueryParameters.GetQueryParameter(
|
||||
|
@ -143,27 +144,25 @@ export default class DetermineTheme {
|
|||
if (json.layers === undefined && json.tagRenderings !== undefined) {
|
||||
// We got fed a layer instead of a theme
|
||||
const layerConfig = <LayerConfigJson>json
|
||||
let icon = Utils.NoNull(
|
||||
layerConfig.pointRendering
|
||||
.flatMap((pr) => pr.marker)
|
||||
.map((iconSpec) => {
|
||||
if (!iconSpec) {
|
||||
return undefined
|
||||
}
|
||||
const icon = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.icon)
|
||||
.render.txt
|
||||
if (
|
||||
iconSpec.color === undefined ||
|
||||
icon.startsWith("http:") ||
|
||||
icon.startsWith("https:")
|
||||
) {
|
||||
return icon
|
||||
}
|
||||
const color = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.color)
|
||||
.render.txt
|
||||
return icon + ":" + color
|
||||
})
|
||||
).join(";")
|
||||
let icon = Lists.noNull(layerConfig.pointRendering
|
||||
.flatMap((pr) => pr.marker)
|
||||
.map((iconSpec) => {
|
||||
if (!iconSpec) {
|
||||
return undefined
|
||||
}
|
||||
const icon = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.icon)
|
||||
.render.txt
|
||||
if (
|
||||
iconSpec.color === undefined ||
|
||||
icon.startsWith("http:") ||
|
||||
icon.startsWith("https:")
|
||||
) {
|
||||
return icon
|
||||
}
|
||||
const color = new TagRenderingConfig(<TagRenderingConfigJson>iconSpec.color)
|
||||
.render.txt
|
||||
return icon + ":" + color
|
||||
})).join(";")
|
||||
|
||||
if (!icon) {
|
||||
icon = "./assets/svg/bug.svg"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Store, UIEventSource } from "../../UIEventSource"
|
||||
import { FeatureSource, IndexedFeatureSource, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Feature } from "geojson"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { OsmFeature } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The featureSourceMerger receives complete geometries from various sources.
|
||||
|
@ -23,7 +23,7 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
constructor(...sources: Src[]) {
|
||||
this._featuresById = new UIEventSource<Map<string, Feature>>(new Map<string, Feature>())
|
||||
this.featuresById = this._featuresById
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
for (const source of sources) {
|
||||
source.features.addCallback(() => {
|
||||
this.addDataFromSources(sources)
|
||||
|
@ -69,7 +69,7 @@ export default class FeatureSourceMerger<Src extends FeatureSource = FeatureSour
|
|||
}
|
||||
|
||||
protected addData(sources: Feature[][]) {
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
let somethingChanged = false
|
||||
const all: Map<string, Feature> = new Map()
|
||||
const unseen = new Set<string>()
|
||||
|
|
|
@ -6,6 +6,7 @@ import BaseUIElement from "../../../UI/BaseUIElement"
|
|||
import { Utils } from "../../../Utils"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
import { FeatureSource } from "../FeatureSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Highly specialized feature source.
|
||||
|
@ -48,11 +49,9 @@ export class LastClickFeatureSource implements FeatureSource {
|
|||
allPresets.push(html)
|
||||
}
|
||||
|
||||
this.renderings = Utils.Dedup(
|
||||
allPresets.map((uiElem) =>
|
||||
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
||||
)
|
||||
)
|
||||
this.renderings = Lists.dedup(allPresets.map((uiElem) =>
|
||||
Utils.runningFromConsole ? "" : uiElem.ConstructElement().innerHTML
|
||||
))
|
||||
|
||||
this._features = new UIEventSource<Feature[]>([])
|
||||
this.features = this._features
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Feature } from "geojson"
|
|||
import FeatureSourceMerger from "../Sources/FeatureSourceMerger"
|
||||
import OsmObjectDownloader from "../../Osm/OsmObjectDownloader"
|
||||
import FullNodeDatabaseSource from "../TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* If a tile is needed (requested via the UIEventSource in the constructor), will download the appropriate tile and pass it via 'handleTile'
|
||||
|
@ -173,7 +174,7 @@ export default class OsmFeatureSource extends FeatureSourceMerger {
|
|||
for (let i = 0; i < features.length; i++) {
|
||||
features[i] = await this.patchIncompleteRelations(features[i], <any>osmJson)
|
||||
}
|
||||
features = Utils.NoNull(features)
|
||||
features = Lists.noNull(features)
|
||||
features.forEach((f) => {
|
||||
f.properties["_backend"] = this._backend
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Utils } from "../../../Utils"
|
|||
import { TagsFilter } from "../../Tags/TagsFilter"
|
||||
import { BBox } from "../../BBox"
|
||||
import { OsmTags } from "../../../Models/OsmFeature"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
("use strict")
|
||||
|
||||
|
@ -199,7 +200,7 @@ export default class OverpassFeatureSource implements UpdatableFeatureSource {
|
|||
*/
|
||||
private GetFilter(interpreterUrl: string, layersToDownload: LayerConfig[]): Overpass {
|
||||
let filters: TagsFilter[] = layersToDownload.map((layer) => layer.source.osmTags)
|
||||
filters = Utils.NoNull(filters)
|
||||
filters = Lists.noNull(filters)
|
||||
if (filters.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Utils } from "../../../Utils"
|
|||
import { Feature, MultiLineString, Position } from "geojson"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { UpdatableDynamicTileSource } from "./DynamicTileSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
|
||||
|
@ -32,7 +33,7 @@ export class LineSourceMerger extends UpdatableDynamicTileSource<
|
|||
}
|
||||
|
||||
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
const all: Map<string, Feature<MultiLineString>> = new Map()
|
||||
const currentZoom = this._zoomlevel?.data ?? 0
|
||||
for (const source of sources) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { FeatureSourceForTile, UpdatableFeatureSource } from "../FeatureSource"
|
||||
import { Store } from "../../UIEventSource"
|
||||
import { BBox } from "../../BBox"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { Feature } from "geojson"
|
||||
import { GeoOperations } from "../../GeoOperations"
|
||||
import { UpdatableDynamicTileSource } from "./DynamicTileSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The PolygonSourceMerger receives various small pieces of bigger polygons and stitches them together.
|
||||
|
@ -29,7 +29,7 @@ export class PolygonSourceMerger extends UpdatableDynamicTileSource<
|
|||
}
|
||||
|
||||
protected addDataFromSources(sources: FeatureSourceForTile[]) {
|
||||
sources = Utils.NoNull(sources)
|
||||
sources = Lists.noNull(sources)
|
||||
const all: Map<string, Feature> = new Map()
|
||||
const zooms: Map<string, number> = new Map()
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from "geojson"
|
||||
import { Tiles } from "../Models/TileRange"
|
||||
import { Utils } from "../Utils"
|
||||
import { Lists } from "../Utils/Lists"
|
||||
|
||||
("use strict")
|
||||
|
||||
|
@ -597,7 +598,7 @@ export class GeoOperations {
|
|||
newFeatures.push(intersectionPart)
|
||||
}
|
||||
}
|
||||
return Utils.NoNull(newFeatures)
|
||||
return Lists.noNull(newFeatures)
|
||||
}
|
||||
|
||||
public static toGpx(
|
||||
|
@ -610,7 +611,7 @@ export class GeoOperations {
|
|||
if (title === undefined || title === "") {
|
||||
title = "Uploaded with MapComplete"
|
||||
}
|
||||
title = Utils.EncodeXmlValue(title)
|
||||
title = Utils.encodeXmlValue(title)
|
||||
const trackPoints: string[] = []
|
||||
let locationsWithMeta: Feature<Point>[]
|
||||
if (Array.isArray(locations)) {
|
||||
|
@ -664,7 +665,7 @@ export class GeoOperations {
|
|||
if (title === undefined || title === "") {
|
||||
title = "Created with MapComplete"
|
||||
}
|
||||
title = Utils.EncodeXmlValue(title)
|
||||
title = Utils.encodeXmlValue(title)
|
||||
const trackPoints: string[] = []
|
||||
for (const l of locations) {
|
||||
let trkpt = ` <wpt lat="${l.geometry.coordinates[1]}" lon="${l.geometry.coordinates[0]}">`
|
||||
|
|
|
@ -8,6 +8,7 @@ import { WikidataImageProvider } from "./WikidataImageProvider"
|
|||
import Panoramax from "./Panoramax"
|
||||
import { Utils } from "../../Utils"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* A generic 'from the interwebz' image picker, without attribution
|
||||
|
@ -101,9 +102,7 @@ export default class AllImageProviders {
|
|||
Mapillary.singleton,
|
||||
AllImageProviders.genericImageProvider,
|
||||
]
|
||||
const allPrefixes = Utils.Dedup(
|
||||
prefixes ?? [].concat(...sources.map((s) => s.defaultKeyPrefixes))
|
||||
)
|
||||
const allPrefixes = Lists.dedup(prefixes ?? [].concat(...sources.map((s) => s.defaultKeyPrefixes)))
|
||||
for (const prefix of allPrefixes) {
|
||||
for (const k in tags) {
|
||||
const v = tags[k]
|
||||
|
@ -149,7 +148,7 @@ export default class AllImageProviders {
|
|||
allSources.push(singleSource)
|
||||
}
|
||||
const source = Stores.concat(allSources).map((result) => {
|
||||
const all = Utils.concat(result)
|
||||
const all = result.flatMap(x => x)
|
||||
return Utils.DedupOnId(all, (i) => [i?.id, i?.url, i?.alt_id])
|
||||
})
|
||||
this._cachedImageStores[cachekey] = source
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Utils } from "../../Utils"
|
|||
import { Feature, Point } from "geojson"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export interface ProvidedImage {
|
||||
url: string
|
||||
|
@ -92,7 +93,7 @@ export default abstract class ImageProvider {
|
|||
) {
|
||||
continue
|
||||
}
|
||||
const values = Utils.NoEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? [])
|
||||
const values = Lists.noEmpty(tags[key]?.split(";")?.map((v) => v.trim()) ?? [])
|
||||
for (const value of values) {
|
||||
if (seenValues.has(value)) {
|
||||
continue
|
||||
|
|
|
@ -15,6 +15,7 @@ import NoteCommentElement from "../../UI/Popup/Notes/NoteCommentElement"
|
|||
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
||||
import ExifReader from "exifreader"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* The ImageUploadManager has a
|
||||
|
@ -176,7 +177,7 @@ export class ImageUploadManager {
|
|||
const failed: Set<ImageUploadArguments> = new Set()
|
||||
this.uploadingAll = true
|
||||
do {
|
||||
queue = Utils.NoNull(this._queue.imagesInQueue.data ?? []).filter(
|
||||
queue = Lists.noNull(this._queue.imagesInQueue.data ?? []).filter(
|
||||
(item) => !failed.has(item)
|
||||
)
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import { Feature, Point } from "geojson"
|
|||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class Mapillary extends ImageProvider {
|
||||
public static readonly singleton = new Mapillary()
|
||||
|
@ -74,11 +75,9 @@ export class Mapillary extends ImageProvider {
|
|||
pKey,
|
||||
}
|
||||
const baselink = `https://www.mapillary.com/app/?`
|
||||
const paramsStr = Utils.NoNull(
|
||||
Object.keys(params).map((k) =>
|
||||
params[k] === undefined ? undefined : k + "=" + params[k]
|
||||
)
|
||||
)
|
||||
const paramsStr = Lists.noNull(Object.keys(params).map((k) =>
|
||||
params[k] === undefined ? undefined : k + "=" + params[k]
|
||||
))
|
||||
return baselink + paramsStr.join("&")
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import { Utils } from "../../Utils"
|
|||
import { Feature, Point } from "geojson"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { ComponentType } from "svelte/types/runtime/internal/dev"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class WikidataImageProvider extends ImageProvider {
|
||||
public static readonly singleton = new WikidataImageProvider()
|
||||
|
@ -13,7 +14,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
public readonly name = "Wikidata"
|
||||
private static readonly keyBlacklist: ReadonlySet<string> = new Set([
|
||||
"mapillary",
|
||||
...Utils.Times((i) => "mapillary:" + i, 10),
|
||||
...Utils.times((i) => "mapillary:" + i, 10),
|
||||
])
|
||||
|
||||
private constructor() {
|
||||
|
@ -60,7 +61,7 @@ export class WikidataImageProvider extends ImageProvider {
|
|||
const promises = WikimediaImageProvider.singleton.ExtractUrls(undefined, commons)
|
||||
allImages.push(promises)
|
||||
}
|
||||
const resolved = await Promise.all(Utils.NoNull(allImages))
|
||||
const resolved = await Promise.all(Lists.noNull(allImages))
|
||||
const flattened = resolved.flatMap((x) => x)
|
||||
if (flattened.length === 1) {
|
||||
flattened[0].originalAttribute = { key, value }
|
||||
|
|
|
@ -7,8 +7,8 @@ import { TagsFilter } from "../../Tags/TagsFilter"
|
|||
import { And } from "../../Tags/And"
|
||||
import { Tag } from "../../Tags/Tag"
|
||||
import { OsmId } from "../../../Models/OsmFeature"
|
||||
import { Utils } from "../../../Utils"
|
||||
import OsmObjectDownloader from "../OsmObjectDownloader"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
export default class DeleteAction extends OsmChangeAction {
|
||||
private readonly _softDeletionTags: TagsFilter
|
||||
|
@ -50,12 +50,12 @@ export default class DeleteAction extends OsmChangeAction {
|
|||
this._softDeletionTags = softDeletionTags
|
||||
} else {
|
||||
this._softDeletionTags = new And(
|
||||
Utils.NoNull([
|
||||
softDeletionTags,
|
||||
new Tag(
|
||||
"fixme",
|
||||
`A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})`
|
||||
),
|
||||
Lists.noNull([
|
||||
softDeletionTags,
|
||||
new Tag(
|
||||
"fixme",
|
||||
`A mapcomplete user marked this feature to be deleted (${meta.specialMotivation})`
|
||||
),
|
||||
])
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import { Utils } from "../../../Utils"
|
|||
import { OsmConnection } from "../OsmConnection"
|
||||
import { Feature, Geometry, LineString, Point } from "geojson"
|
||||
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
export default class ReplaceGeometryAction extends OsmChangeAction implements PreviewableAction {
|
||||
/**
|
||||
|
@ -164,7 +165,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
preview.push(feature)
|
||||
})
|
||||
|
||||
return StaticFeatureSource.fromGeojson(Utils.NoNull(preview))
|
||||
return StaticFeatureSource.fromGeojson(Lists.noNull(preview))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -317,7 +318,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
|
|||
candidate = undefined
|
||||
moveDistance = Infinity
|
||||
distances.forEach((distances, nodeId) => {
|
||||
const minDist = Math.min(...Utils.NoNull(distances))
|
||||
const minDist = Math.min(...(Lists.noNull(distances)))
|
||||
if (moveDistance > minDist) {
|
||||
// We have found a candidate to move
|
||||
candidate = nodeId
|
||||
|
|
|
@ -18,6 +18,7 @@ import DeleteAction from "./Actions/DeleteAction"
|
|||
import MarkdownUtils from "../../Utils/MarkdownUtils"
|
||||
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
||||
import { Feature, Point } from "geojson"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Handles all changes made to OSM.
|
||||
|
@ -260,7 +261,7 @@ export class Changes {
|
|||
}
|
||||
|
||||
public static GetNeededIds(changes: ChangeDescription[]) {
|
||||
return Utils.Dedup(changes.filter((c) => c.id >= 0).map((c) => c.type + "/" + c.id))
|
||||
return Lists.dedup(changes.filter((c) => c.id >= 0).map((c) => c.type + "/" + c.id))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -467,8 +468,8 @@ export class Changes {
|
|||
if (change.changes !== undefined) {
|
||||
switch (change.type) {
|
||||
case "node": {
|
||||
const nlat = Utils.Round7(change.changes.lat)
|
||||
const nlon = Utils.Round7(change.changes.lon)
|
||||
const nlat = Utils.round7(change.changes.lat)
|
||||
const nlon = Utils.round7(change.changes.lon)
|
||||
const n = <OsmNode>obj
|
||||
if (n.lat !== nlat || n.lon !== nlon) {
|
||||
n.lat = nlat
|
||||
|
@ -717,11 +718,9 @@ export class Changes {
|
|||
* We _do not_ pass in the Changes object itself - we want the data from OSM directly in order to apply the changes
|
||||
*/
|
||||
const downloader = new OsmObjectDownloader(this.backend, undefined)
|
||||
const osmObjects = Utils.NoNull(
|
||||
await Promise.all<{ id: string; osmObj: OsmObject | "deleted" }>(
|
||||
neededIds.map((id) => this.getOsmObject(id, downloader))
|
||||
)
|
||||
)
|
||||
const osmObjects = Lists.noNull(await Promise.all<{ id: string; osmObj: OsmObject | "deleted" }>(
|
||||
neededIds.map((id) => this.getOsmObject(id, downloader))
|
||||
))
|
||||
|
||||
// Drop changes to deleted items
|
||||
for (const { osmObj, id } of osmObjects) {
|
||||
|
@ -801,7 +800,7 @@ export class Changes {
|
|||
value: descr.meta.specialMotivation,
|
||||
}))
|
||||
|
||||
const distances = Utils.NoNull(pending.map((descr) => descr.meta.distanceToObject))
|
||||
const distances = Lists.noNull(pending.map((descr) => descr.meta.distanceToObject))
|
||||
distances.sort((a, b) => a - b)
|
||||
const perBinCount = Constants.distanceToChangeObjectBins.map(() => 0)
|
||||
|
||||
|
@ -816,23 +815,21 @@ export class Changes {
|
|||
}
|
||||
}
|
||||
|
||||
const perBinMessage = Utils.NoNull(
|
||||
perBinCount.map((count, i) => {
|
||||
if (count === 0) {
|
||||
return undefined
|
||||
}
|
||||
const maxD = maxDistances[i]
|
||||
let key = `change_within_${maxD}m`
|
||||
if (maxD === Number.MAX_VALUE) {
|
||||
key = `change_over_${maxDistances[i - 1]}m`
|
||||
}
|
||||
return {
|
||||
key,
|
||||
value: count,
|
||||
aggregate: true,
|
||||
}
|
||||
})
|
||||
)
|
||||
const perBinMessage = Lists.noNull(perBinCount.map((count, i) => {
|
||||
if (count === 0) {
|
||||
return undefined
|
||||
}
|
||||
const maxD = maxDistances[i]
|
||||
let key = `change_within_${maxD}m`
|
||||
if (maxD === Number.MAX_VALUE) {
|
||||
key = `change_over_${maxDistances[i - 1]}m`
|
||||
}
|
||||
return {
|
||||
key,
|
||||
value: count,
|
||||
aggregate: true,
|
||||
}
|
||||
}))
|
||||
|
||||
// This method is only called with changedescriptions for this theme
|
||||
const theme = pending[0].meta.theme
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Utils } from "../../Utils"
|
|||
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
|
||||
import { AndroidPolyfill } from "../Web/AndroidPolyfill"
|
||||
import ImageUploadQueue from "../ImageProviders/ImageUploadQueue"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export interface ChangesetTag {
|
||||
key: string
|
||||
|
@ -393,7 +394,7 @@ export class ChangesetHandler {
|
|||
private async UpdateTags(csId: number, tags: ChangesetTag[]) {
|
||||
tags = ChangesetHandler.removeDuplicateMetaTags(tags)
|
||||
|
||||
tags = Utils.NoNull(tags).filter(
|
||||
tags = Lists.noNull(tags).filter(
|
||||
(tag) =>
|
||||
tag.key !== undefined &&
|
||||
tag.value !== undefined &&
|
||||
|
|
|
@ -150,7 +150,7 @@ export abstract class OsmObject {
|
|||
}
|
||||
const v = this.tags[key]
|
||||
if (v !== "" && v !== undefined) {
|
||||
tags += ` <tag k="${Utils.EncodeXmlValue(key)}" v="${Utils.EncodeXmlValue(
|
||||
tags += ` <tag k="${Utils.encodeXmlValue(key)}" v="${Utils.encodeXmlValue(
|
||||
this.tags[key]
|
||||
)}"/>
|
||||
`
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Store, UIEventSource } from "../UIEventSource"
|
|||
import { OsmConnection } from "./OsmConnection"
|
||||
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
import OSMAuthInstance = OSMAuth.osmAuth
|
||||
|
||||
export class OsmPreferences {
|
||||
|
@ -270,7 +271,7 @@ export class OsmPreferences {
|
|||
return
|
||||
}
|
||||
// _All_ keys are deleted first, to avoid pending parts
|
||||
const keysToDelete = Utils.Dedup(OsmPreferences.keysStartingWith(this.seenKeys, k))
|
||||
const keysToDelete = Lists.dedup(OsmPreferences.keysStartingWith(this.seenKeys, k))
|
||||
if (v === null || v === undefined || v === "" || v === "undefined" || v === "null") {
|
||||
for (const k of keysToDelete) {
|
||||
await this.deleteKeyDirectly(k)
|
||||
|
|
|
@ -13,7 +13,7 @@ export default class CombinedSearcher implements GeocodingProvider {
|
|||
* @param providers
|
||||
*/
|
||||
constructor(...providers: ReadonlyArray<GeocodingProvider>) {
|
||||
this._providers = Utils.NoNull(providers)
|
||||
this._providers = Utils.noNull(providers)
|
||||
this._providersWithSuggest = this._providers.filter((pr) => pr.suggest !== undefined)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import GeocodingProvider, { GeocodeResult } from "./GeocodingProvider"
|
||||
import { Utils } from "../../Utils"
|
||||
import { ImmutableStore, Store } from "../UIEventSource"
|
||||
import CoordinateParser from "coordinate-parser"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* A simple search-class which interprets possible locations
|
||||
|
@ -71,13 +71,11 @@ export default class CoordinateSearch implements GeocodingProvider {
|
|||
* results[0] // => {lat: 51.047977, lon: 3.51184, "display_name": "lon: 3.51184, lat: 51.047977", "category": "coordinate","osm_id": "3.51184/51.047977", "source": "coordinate:latlon"}
|
||||
*/
|
||||
private directSearch(query: string): GeocodeResult[] {
|
||||
const matches = Utils.NoNull(CoordinateSearch.latLonRegexes.map((r) => query.match(r))).map(
|
||||
const matches = Lists.noNull(CoordinateSearch.latLonRegexes.map((r) => query.match(r))).map(
|
||||
(m) => CoordinateSearch.asResult(m[2], m[1], "latlon")
|
||||
)
|
||||
|
||||
const matchesLonLat = Utils.NoNull(
|
||||
CoordinateSearch.lonLatRegexes.map((r) => query.match(r))
|
||||
).map((m) => CoordinateSearch.asResult(m[1], m[2], "lonlat"))
|
||||
const matchesLonLat = Lists.noNull(CoordinateSearch.lonLatRegexes.map((r) => query.match(r))).map((m) => CoordinateSearch.asResult(m[1], m[2], "lonlat"))
|
||||
const init = matches.concat(matchesLonLat)
|
||||
if (init.length > 0) {
|
||||
return init
|
||||
|
|
|
@ -4,6 +4,7 @@ import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/Filte
|
|||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import LayerState from "../State/LayerState"
|
||||
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export type FilterSearchResult = {
|
||||
option: FilterConfigOption
|
||||
|
@ -64,7 +65,7 @@ export default class FilterSearch {
|
|||
].flatMap((term) => [term, ...(term?.split(" ") ?? [])])
|
||||
terms = terms.map((t) => Utils.simplifyStringForSearch(t))
|
||||
terms.push(option.emoji)
|
||||
Utils.NoNullInplace(terms)
|
||||
Lists.noNullInplace(terms)
|
||||
const distances = queries.flatMap((query) =>
|
||||
terms.map((entry) => {
|
||||
const d = Utils.levenshteinDistance(query, entry.slice(0, query.length))
|
||||
|
|
|
@ -5,6 +5,7 @@ import { Feature } from "geojson"
|
|||
import { GeoOperations } from "../GeoOperations"
|
||||
import { ImmutableStore, Store, Stores } from "../UIEventSource"
|
||||
import OpenStreetMapIdSearch from "./OpenStreetMapIdSearch"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
type IntermediateResult = {
|
||||
feature: Feature
|
||||
|
@ -41,13 +42,13 @@ export default class LocalElementSearch implements GeocodingProvider {
|
|||
|
||||
for (const feature of features) {
|
||||
const props = feature.properties
|
||||
const searchTerms: string[] = Utils.NoNull([
|
||||
props.name,
|
||||
props.alt_name,
|
||||
props.local_name,
|
||||
props["addr:street"] && props["addr:number"]
|
||||
? props["addr:street"] + props["addr:number"]
|
||||
: undefined,
|
||||
const searchTerms: string[] = Lists.noNull([
|
||||
props.name,
|
||||
props.alt_name,
|
||||
props.local_name,
|
||||
props["addr:street"] && props["addr:number"]
|
||||
? props["addr:street"] + props["addr:number"]
|
||||
: undefined,
|
||||
])
|
||||
|
||||
let levehnsteinD: number
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import GeocodingProvider, { GeocodeResult, GeocodingOptions } from "./GeocodingProvider"
|
||||
import { OsmId } from "../../Models/OsmFeature"
|
||||
import { Utils } from "../../Utils"
|
||||
import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class OpenStreetMapIdSearch implements GeocodingProvider {
|
||||
private static readonly regex =
|
||||
|
@ -76,7 +76,7 @@ export default class OpenStreetMapIdSearch implements GeocodingProvider {
|
|||
async search(query: string, _: GeocodingOptions): Promise<GeocodeResult[]> {
|
||||
if (!isNaN(Number(query))) {
|
||||
const n = Number(query)
|
||||
return Utils.NoNullInplace(
|
||||
return Lists.noNullInplace(
|
||||
await Promise.all([
|
||||
this.getInfoAbout(`node/${n}`).catch(() => undefined),
|
||||
this.getInfoAbout(`way/${n}`).catch(() => undefined),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Locale from "../../UI/i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
import ThemeSearch from "./ThemeSearch"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class SearchUtils {
|
||||
/** Applies special search terms, such as 'studio', 'osmcha', ...
|
||||
|
@ -66,7 +67,7 @@ export default class SearchUtils {
|
|||
} else {
|
||||
terms = (keywords[language] ?? []).concat(keywords["*"])
|
||||
}
|
||||
const termsAll = Utils.NoNullInplace(terms).flatMap((t) => t.split(" "))
|
||||
const termsAll = Lists.noNullInplace(terms).flatMap((t) => t.split(" "))
|
||||
|
||||
let distanceSummed = 0
|
||||
for (let i = 0; i < queryParts.length; i++) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import Fuse, { IFuseOptions } from "fuse.js"
|
|||
import Constants from "../../Models/Constants"
|
||||
import Locale from "../../UI/i18n/Locale"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class ThemeSearchIndex {
|
||||
private readonly themeIndex: Fuse<MinimalThemeInformation>
|
||||
|
@ -18,7 +19,7 @@ export class ThemeSearchIndex {
|
|||
themesToSearch?: MinimalThemeInformation[],
|
||||
layersToIgnore: string[] = []
|
||||
) {
|
||||
const themes = Utils.NoNull(themesToSearch ?? ThemeSearch.officialThemes?.themes)
|
||||
const themes = Utils.noNull(themesToSearch ?? ThemeSearch.officialThemes?.themes)
|
||||
if (!themes) {
|
||||
throw "No themes loaded. Did generate:layeroverview fail?"
|
||||
}
|
||||
|
@ -100,7 +101,7 @@ export class ThemeSearchIndex {
|
|||
const knownHidden: Store<string[]> = Stores.listStabilized(
|
||||
UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection)
|
||||
.stabilized(1000)
|
||||
.map((list) => Utils.Dedup(list))
|
||||
.map((list) => Lists.dedup(list))
|
||||
)
|
||||
const otherThemes: MinimalThemeInformation[] = ThemeSearch.officialThemes.themes.filter(
|
||||
(th) => th.id !== state.theme.id
|
||||
|
|
|
@ -18,8 +18,8 @@ import { Feature } from "geojson"
|
|||
import OpenLocationCodeSearch from "../Search/OpenLocationCodeSearch"
|
||||
import { BBox } from "../BBox"
|
||||
import { QueryParameters } from "../Web/QueryParameters"
|
||||
import { Utils } from "../../Utils"
|
||||
import { NominatimGeocoding } from "../Search/NominatimGeocoding"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class SearchState {
|
||||
public readonly feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
|
||||
|
@ -83,7 +83,7 @@ export default class SearchState {
|
|||
}
|
||||
})))
|
||||
this.runningEngines = isRunningPerEngine.bindD(
|
||||
listOfSources => Stores.concat(listOfSources).mapD(list => Utils.NoNull(list)))
|
||||
listOfSources => Stores.concat(listOfSources).mapD(list => Lists.noNull(list)))
|
||||
|
||||
|
||||
this.failedEngines = suggestionsListWithSource
|
||||
|
@ -102,7 +102,7 @@ export default class SearchState {
|
|||
return []
|
||||
}
|
||||
}),
|
||||
))).map(list => Utils.NoNull(list?.flatMap(x => x) ?? []))
|
||||
))).map(list => Lists.noNull(list?.flatMap(x => x) ?? []))
|
||||
|
||||
this.suggestionsSearchRunning = this.runningEngines.map(running => running?.length > 0)
|
||||
this.suggestions = suggestionsList.bindD((suggestions) =>
|
||||
|
|
|
@ -20,6 +20,7 @@ import Showdown from "showdown"
|
|||
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
||||
import { GeocodeResult } from "../Search/GeocodingProvider"
|
||||
import Translations from "../../UI/i18n/Translations"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
class RoundRobinStore<T> {
|
||||
private readonly _store: UIEventSource<T[]>
|
||||
|
@ -41,7 +42,7 @@ class RoundRobinStore<T> {
|
|||
private set() {
|
||||
const v = this._store.data
|
||||
const i = this._index.data
|
||||
const newList = Utils.NoNull(v.slice(i + 1, v.length).concat(v.slice(0, i + 1)))
|
||||
const newList = Lists.noNull(v.slice(i + 1, v.length).concat(v.slice(0, i + 1)))
|
||||
if (newList.length === 0) {
|
||||
this._index.set(0)
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ export class OptionallySyncedHistory<T extends object | string> {
|
|||
defaultValue: "sync",
|
||||
})
|
||||
|
||||
this.syncedBackingStore = UIEventSource.concat(Utils.TimesT(maxHistory, (i) => {
|
||||
this.syncedBackingStore = UIEventSource.concat(Utils.timesT(maxHistory, (i) => {
|
||||
const pref = osmconnection.getPreference(key + "-hist-" + i + "-")
|
||||
return UIEventSource.asObject<T>(pref, undefined)
|
||||
}))
|
||||
|
@ -555,27 +556,25 @@ export default class UserRelatedState {
|
|||
|
||||
const untranslated = missing.untranslated.get(language) ?? []
|
||||
const hasMissingTheme = untranslated.some((k) => k.startsWith("themes:"))
|
||||
const missingLayers = Utils.Dedup(
|
||||
untranslated
|
||||
.filter((k) => k.startsWith("layers:"))
|
||||
.map((k) => k.slice("layers:".length).split(".")[0])
|
||||
)
|
||||
const missingLayers = Lists.dedup(untranslated
|
||||
.filter((k) => k.startsWith("layers:"))
|
||||
.map((k) => k.slice("layers:".length).split(".")[0]))
|
||||
|
||||
const zenLinks: { link: string; id: string }[] = Utils.NoNull([
|
||||
hasMissingTheme
|
||||
? {
|
||||
id: "theme:" + layout.id,
|
||||
link: Translations.hrefToWeblateZen(
|
||||
language,
|
||||
"themes",
|
||||
layout.id
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
...missingLayers.map((id) => ({
|
||||
id: "layer:" + id,
|
||||
link: Translations.hrefToWeblateZen(language, "layers", id),
|
||||
})),
|
||||
const zenLinks: { link: string; id: string }[] = Lists.noNull([
|
||||
hasMissingTheme
|
||||
? {
|
||||
id: "theme:" + layout.id,
|
||||
link: Translations.hrefToWeblateZen(
|
||||
language,
|
||||
"themes",
|
||||
layout.id
|
||||
),
|
||||
}
|
||||
: undefined,
|
||||
...missingLayers.map((id) => ({
|
||||
id: "layer:" + id,
|
||||
link: Translations.hrefToWeblateZen(language, "layers", id),
|
||||
})),
|
||||
])
|
||||
const untranslated_count = untranslated.length
|
||||
amendedPrefs.data["_translation_total"] = "" + total
|
||||
|
|
|
@ -11,6 +11,7 @@ import key_counts from "../../assets/key_totals.json"
|
|||
|
||||
import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext"
|
||||
import { FlatTag, TagsFilterClosed, UploadableTag } from "./TagTypes"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
type Tags = Record<string, string>
|
||||
|
||||
|
@ -383,7 +384,7 @@ export class TagUtils {
|
|||
const keyValues = TagUtils.SplitKeys(tagsFilters)
|
||||
const and: UploadableTag[] = []
|
||||
for (const key in keyValues) {
|
||||
const values = Utils.Dedup(keyValues[key]).filter((v) => v !== "")
|
||||
const values = Lists.dedup(keyValues[key]).filter((v) => v !== "")
|
||||
values.sort()
|
||||
and.push(new Tag(key, values.join(";")))
|
||||
}
|
||||
|
@ -742,7 +743,7 @@ export class TagUtils {
|
|||
if (level === undefined || level === null) {
|
||||
return []
|
||||
}
|
||||
let spec = Utils.NoNull([level])
|
||||
let spec = Lists.noNull([level])
|
||||
spec = [].concat(...spec.map((s) => s?.split(";")))
|
||||
spec = [].concat(
|
||||
...spec.map((s) => {
|
||||
|
@ -764,7 +765,7 @@ export class TagUtils {
|
|||
return values
|
||||
})
|
||||
)
|
||||
return Utils.NoNull(spec)
|
||||
return Lists.noNull(spec)
|
||||
}
|
||||
|
||||
private static ParseTagUnsafe(json: TagConfigJson, context: string = ""): TagsFilterClosed {
|
||||
|
@ -919,10 +920,10 @@ export class TagUtils {
|
|||
|
||||
public static GetPopularity(tag: TagsFilter): number | undefined {
|
||||
if (tag instanceof And) {
|
||||
return Math.min(...Utils.NoNull(tag.and.map((t) => TagUtils.GetPopularity(t)))) - 1
|
||||
return Math.min(...(Lists.noNull(tag.and.map((t) => TagUtils.GetPopularity(t))))) - 1
|
||||
}
|
||||
if (tag instanceof Or) {
|
||||
return Math.max(...Utils.NoNull(tag.or.map((t) => TagUtils.GetPopularity(t)))) + 1
|
||||
return Math.max(...(Lists.noNull(tag.or.map((t) => TagUtils.GetPopularity(t))))) + 1
|
||||
}
|
||||
if (tag instanceof Tag) {
|
||||
return TagUtils.GetCount(tag.key, tag.value)
|
||||
|
|
|
@ -9,6 +9,7 @@ import { RegexTag } from "../Tags/RegexTag"
|
|||
import { TagConfigJson } from "../../Models/ThemeConfig/Json/TagConfigJson"
|
||||
import { TagUtils } from "../Tags/TagUtils"
|
||||
import Constants from "../../Models/Constants"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* Main name suggestion index file
|
||||
|
@ -103,7 +104,7 @@ export default class NameSuggestionIndex {
|
|||
}
|
||||
})
|
||||
)
|
||||
stats = Utils.NoNull(stats)
|
||||
stats = Lists.noNull(stats)
|
||||
if (stats.length === 1) {
|
||||
return stats[0]
|
||||
}
|
||||
|
@ -282,7 +283,7 @@ export default class NameSuggestionIndex {
|
|||
}
|
||||
const keys = Object.keys(this.nsiFile.nsi)
|
||||
const all = keys.map((k) => this.nsiFile.nsi[k].properties.path.split("/")[0])
|
||||
this._supportedTypes = Utils.Dedup(all).map((s) => {
|
||||
this._supportedTypes = Lists.dedup(all).map((s) => {
|
||||
if (s.endsWith("s")) {
|
||||
s = s.substring(0, s.length - 1)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Utils } from "../../Utils"
|
|||
import { Store, UIEventSource } from "../UIEventSource"
|
||||
import { SimplifiedClaims, WBK } from "wikibase-sdk"
|
||||
import { ServerSourceInfo } from "../../Models/SourceOverview"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export class WikidataResponse {
|
||||
public readonly id: string
|
||||
|
@ -294,7 +295,7 @@ export default class Wikidata {
|
|||
}
|
||||
})
|
||||
)
|
||||
return Utils.NoNull(maybeResponses.map((r) => <WikidataResponse>r["success"]))
|
||||
return Lists.noNull(maybeResponses.map((r) => <WikidataResponse>r["success"]))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
]),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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) =>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
])
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
import ThemesList from "./BigComponents/ThemesList.svelte"
|
||||
import { MinimalThemeInformation } from "../Models/ThemeConfig/ThemeConfig"
|
||||
import LoginButton from "./Base/LoginButton.svelte"
|
||||
import { Utils } from "../Utils"
|
||||
import Searchbar from "./Base/Searchbar.svelte"
|
||||
import ThemeSearch, { ThemeSearchIndex } from "../Logic/Search/ThemeSearch"
|
||||
import SearchUtils from "../Logic/Search/SearchUtils"
|
||||
|
@ -26,6 +25,7 @@
|
|||
import AccordionSingle from "./Flowbite/AccordionSingle.svelte"
|
||||
import MenuDrawerIndex from "./BigComponents/MenuDrawerIndex.svelte"
|
||||
import InsetSpacer from "./Base/InsetSpacer.svelte"
|
||||
import { Lists } from "../Utils/Lists"
|
||||
|
||||
AndroidPolyfill.init().then(() => console.log("Android polyfill setup completed"))
|
||||
const featureSwitches = new OsmConnectionFeatureSwitches()
|
||||
|
@ -80,7 +80,7 @@
|
|||
|
||||
const customThemes: Store<MinimalThemeInformation[]> = Stores.listStabilized<string>(
|
||||
state.installedUserThemes.stabilized(1000)
|
||||
).mapD((stableIds) => Utils.NoNullInplace(stableIds.map((id) => state.getUnofficialTheme(id))))
|
||||
).mapD((stableIds) => Lists.noNullInplace(stableIds.map((id) => state.getUnofficialTheme(id))))
|
||||
|
||||
function filtered(themes: Store<MinimalThemeInformation[]>): Store<MinimalThemeInformation[]> {
|
||||
const searchIndex = Locale.language.map(
|
||||
|
|
|
@ -3,8 +3,7 @@ import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
|
|||
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
||||
import { OsmFeature } from "../../Models/OsmFeature"
|
||||
import { Utils } from "../../Utils"
|
||||
import { labels } from "wikibase-sdk/dist/src/helpers/simplify"
|
||||
import { isInteger } from "tailwind-merge/dist/lib/validators"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
class ChartJsColours {
|
||||
public static readonly unknownColor = "rgba(128, 128, 128, 0.2)"
|
||||
|
@ -243,7 +242,7 @@ export class ChartJsUtils {
|
|||
if (options?.period === "month") {
|
||||
trimmedDays = trimmedDays.map((d) => d.substring(0, 7))
|
||||
}
|
||||
trimmedDays = Utils.Dedup(trimmedDays)
|
||||
trimmedDays = Lists.dedup(trimmedDays)
|
||||
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
const label = labels[i]
|
||||
|
|
|
@ -10,7 +10,7 @@ export default class Combine extends BaseUIElement {
|
|||
|
||||
constructor(uiElements: (string | BaseUIElement)[]) {
|
||||
super()
|
||||
this.uiElements = Utils.NoNull(uiElements).map((el) => {
|
||||
this.uiElements = Utils.noNull(uiElements).map((el) => {
|
||||
if (typeof el === "string") {
|
||||
return new FixedUiElement(el)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Translation } from "../i18n/Translation"
|
|||
import Translations from "../i18n/Translations"
|
||||
import MarkdownUtils from "../../Utils/MarkdownUtils"
|
||||
import Locale from "../i18n/Locale"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export default class Hotkeys {
|
||||
public static readonly _docs: UIEventSource<
|
||||
|
@ -130,7 +131,7 @@ export default class Hotkeys {
|
|||
]
|
||||
})
|
||||
.sort()
|
||||
byKey = Utils.NoNull(byKey)
|
||||
byKey = Lists.noNull(byKey)
|
||||
for (let i = byKey.length - 1; i > 0; i--) {
|
||||
if (byKey[i - 1][0] === byKey[i][0]) {
|
||||
byKey.splice(i, 1)
|
||||
|
|
|
@ -64,7 +64,7 @@ export default class TableOfContents {
|
|||
}
|
||||
}
|
||||
|
||||
const heading = Utils.Times(() => "#", firstTitle.depth)
|
||||
const heading = Utils.times(() => "#", firstTitle.depth)
|
||||
toc = heading + " Table of contents\n\n" + toc
|
||||
|
||||
const firstTitleIndex = md.indexOf(firstTitle.title)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import type SmallLicense from "../../Models/smallLicense"
|
||||
import { Utils } from "../../Utils"
|
||||
import { twJoin } from "tailwind-merge"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export let iconPath: string
|
||||
export let license: SmallLicense
|
||||
|
@ -10,7 +11,7 @@
|
|||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
let sources = Utils.NoNull(Utils.NoEmpty(license?.sources))
|
||||
let sources = Utils.noNull(Lists.noEmpty(license?.sources))
|
||||
|
||||
function sourceName(lnk: string) {
|
||||
try {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import type { ChartConfiguration } from "chart.js"
|
||||
import ChartJs from "../Base/ChartJs.svelte"
|
||||
import { ChartJsUtils } from "../Base/ChartJsUtils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export let values: Store<string[]>
|
||||
let counts: Store<Map<string, number>> = values.map(
|
||||
|
@ -11,7 +12,7 @@
|
|||
return undefined
|
||||
}
|
||||
|
||||
values = Utils.NoNull(values)
|
||||
values = Utils.noNull(values)
|
||||
const counts = new Map<string, number>()
|
||||
for (const value of values) {
|
||||
const c = counts.get(value) ?? 0
|
||||
|
@ -23,7 +24,7 @@
|
|||
|
||||
let max: Store<number> = counts.mapD(counts => Math.max(...Array.from(counts.values())))
|
||||
let keys: Store<string> = counts.mapD(counts => {
|
||||
const keys = Utils.Dedup(counts.keys())
|
||||
const keys = Lists.dedup(counts.keys())
|
||||
keys.sort(/*inplace sort*/)
|
||||
return keys
|
||||
})
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
import SnappingFeatureSource from "../../Logic/FeatureSource/Sources/SnappingFeatureSource"
|
||||
import FeatureSourceMerger from "../../Logic/FeatureSource/Sources/FeatureSourceMerger"
|
||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
|
||||
import { Utils } from "../../Utils"
|
||||
import Move_arrows from "../../assets/svg/Move_arrows.svelte"
|
||||
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
|
||||
import { Tag } from "../../Logic/Tags/Tag"
|
||||
import { TagUtils } from "../../Logic/Tags/TagUtils"
|
||||
import type { WayId } from "../../Models/OsmFeature"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
/**
|
||||
* An advanced location input, which has support to:
|
||||
|
@ -114,7 +114,7 @@
|
|||
})
|
||||
}
|
||||
const snappedLocation = new SnappingFeatureSource(
|
||||
new FeatureSourceMerger(...Utils.NoNull(snapSources)),
|
||||
new FeatureSourceMerger(...(Lists.noNull(snapSources))),
|
||||
// We snap to the (constantly updating) map location
|
||||
mapProperties.location,
|
||||
{
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
import { QueryParameters } from "../../Logic/Web/QueryParameters"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import Translations from "../i18n/Translations"
|
||||
import { Utils } from "../../Utils"
|
||||
import Qr from "../../Utils/Qr"
|
||||
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
|
||||
import Constants from "../../Models/Constants"
|
||||
import Copyable from "../Base/Copyable.svelte"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export let state: ThemeViewState
|
||||
const tr = Translations.t.general.sharescreen
|
||||
|
@ -45,7 +45,7 @@
|
|||
enableGeolocation: boolean
|
||||
) {
|
||||
const layout = state.theme
|
||||
let excluded = Utils.NoNull([
|
||||
let excluded = Lists.noNull([
|
||||
showWelcomeMessage ? undefined : "fs-welcome-message",
|
||||
enableLogin ? undefined : "fs-enable-login",
|
||||
enableFilters ? undefined : "fs-filter",
|
||||
|
@ -79,7 +79,7 @@
|
|||
.filter((part) => !part.startsWith("layer-"))
|
||||
.concat(...layers)
|
||||
.concat(excluded.map((k) => k + "=" + false))
|
||||
linkToShare = baseLink + Utils.Dedup(params).join("&")
|
||||
linkToShare = baseLink + Lists.dedup(params).join("&")
|
||||
|
||||
if (layout.definitionRaw !== undefined) {
|
||||
linkToShare += "&userlayout=" + (layout.definedAtUrl ?? layout.id)
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
? "flex flex-wrap items-center justify-center gap-x-2"
|
||||
: "theme-list my-2 gap-4 md:grid md:grid-flow-row md:grid-cols-2 lg:grid-cols-3"}
|
||||
>
|
||||
{#each Utils.DedupOnId(Utils.NoNull(themes)) as theme (theme.id)}
|
||||
{#each Utils.DedupOnId(Utils.noNull(themes)) as theme (theme.id)}
|
||||
<ThemeButton {theme} {state} iconOnly={onlyIcons}>
|
||||
{#if $search && hasSelection && themes?.[0] === theme}
|
||||
<span class="thanks hidden-on-mobile" aria-hidden="true">
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
}
|
||||
return trs.find((tr) =>
|
||||
tr.mappings.some((mapping) => {
|
||||
const ifTags = Or.construct(Utils.NoNull([mapping.if, mapping.alsoShowIf]))
|
||||
const ifTags = Or.construct(Lists.noNull([mapping.if, mapping.alsoShowIf]))
|
||||
const keys = ifTags.usedKeys()
|
||||
return keys.some((k) => k == key)
|
||||
})
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
*/
|
||||
import { Stores, UIEventSource } from "../../../../Logic/UIEventSource"
|
||||
import SingleCollectionTime from "./SingleCollectionTime.svelte"
|
||||
import { Utils } from "../../../../Utils"
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import { Lists } from "../../../../Utils/Lists"
|
||||
|
||||
export let value: UIEventSource<string>
|
||||
|
||||
let initialRules: string[] = Utils.NoEmpty(value.data?.split(";")?.map(v => v.trim()))
|
||||
let initialRules: string[] = Lists.noEmpty(value.data?.split(";")?.map(v => v.trim()))
|
||||
let singleRules: UIEventSource<UIEventSource<string>[]> = new UIEventSource(
|
||||
initialRules?.map(v => new UIEventSource(v)) ?? []
|
||||
)
|
||||
|
@ -17,7 +17,7 @@ if(singleRules.data.length === 0){
|
|||
}
|
||||
singleRules.bindD(stores => Stores.concat(stores)).addCallbackAndRunD(subrules => {
|
||||
console.log("Setting subrules", subrules)
|
||||
value.set(Utils.NoEmpty(subrules).join("; "))
|
||||
value.set(Lists.noEmpty(subrules).join("; "))
|
||||
})
|
||||
|
||||
function rm(rule: UIEventSource){
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
|
||||
import PlusCircle from "@rgossiaux/svelte-heroicons/solid/PlusCircle"
|
||||
import TimeInput from "../TimeInput.svelte"
|
||||
import { TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import Checkbox from "../../../Base/Checkbox.svelte"
|
||||
|
@ -8,7 +7,8 @@
|
|||
import { Stores, UIEventSource } from "../../../../Logic/UIEventSource"
|
||||
import Translations from "../../../i18n/Translations"
|
||||
import { OH } from "../../../OpeningHours/OpeningHours"
|
||||
import { Utils } from "../../../../Utils"
|
||||
import { Lists } from "../../../../Utils/Lists"
|
||||
import { Translation } from "../../../i18n/Translation"
|
||||
|
||||
export let value: UIEventSource<string>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
|||
*/
|
||||
let weekdays: Translation[] = [wt.monday, wt.tuesday, wt.wednesday, wt.thursday, wt.friday, wt.saturday, wt.sunday, Translations.t.general.opening_hours.ph]
|
||||
|
||||
let initialTimes= Utils.NoEmpty(value.data?.split(" ")?.[1]?.split(",")?.map(s => s.trim())??[])
|
||||
let initialTimes= Lists.noEmpty(value.data?.split(" ")?.[1]?.split(",")?.map(s => s.trim()) ?? [])
|
||||
let values = new UIEventSource(initialTimes.map(t => new UIEventSource(t)))
|
||||
if(values.data.length === 0){
|
||||
values.data.push(new UIEventSource(""))
|
||||
|
@ -29,7 +29,7 @@
|
|||
let daysOfTheWeek = [...OH.days, "PH"]
|
||||
let selectedDays = daysOfTheWeek.map(() => new UIEventSource(false))
|
||||
|
||||
let initialDays = Utils.NoEmpty(value.data?.split(" ")?.[0]?.split(",") ?? [])
|
||||
let initialDays = Lists.noEmpty(value.data?.split(" ")?.[0]?.split(",") ?? [])
|
||||
for (const initialDay of initialDays) {
|
||||
if (initialDay.indexOf("-") > 0) {
|
||||
let [start, end] = initialDay.split("-")
|
||||
|
@ -48,17 +48,17 @@
|
|||
}
|
||||
|
||||
let selectedDaysBound = Stores.concat(selectedDays)
|
||||
.mapD(days => Utils.NoNull(days.map((selected, i) => selected ? daysOfTheWeek[i] : undefined)))
|
||||
.mapD(days => Lists.noNull(days.map((selected, i) => selected ? daysOfTheWeek[i] : undefined)))
|
||||
let valuesConcat: Store<string[]> = values.bindD(values => Stores.concat(values))
|
||||
.mapD(values => Utils.NoEmpty(values))
|
||||
.mapD(values => Lists.noEmpty(values))
|
||||
valuesConcat.mapD(times => {
|
||||
console.log(times)
|
||||
times = Utils.NoNull(times)
|
||||
times = Lists.noNull(times)
|
||||
if (!times || times?.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
times?.sort(/*concatted, new array*/)
|
||||
return (Utils.NoEmpty(selectedDaysBound.data).join(",") + " " + times).trim()
|
||||
return (Lists.noEmpty(selectedDaysBound.data).join(",") + " " + times).trim()
|
||||
}, [selectedDaysBound]).addCallbackAndRunD(v => value.set(v))
|
||||
|
||||
function selectWeekdays() {
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
let element: HTMLTableElement
|
||||
|
||||
function range(n: number) {
|
||||
return Utils.TimesT(n, (n) => n)
|
||||
return Utils.timesT(n, (n) => n)
|
||||
}
|
||||
|
||||
function clearSelection() {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import { Utils } from "../../../Utils"
|
||||
import WikidataValidator from "../Validators/WikidataValidator"
|
||||
import Searchbar from "../../Base/Searchbar.svelte"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
const t = Translations.t.general.wikipedia
|
||||
|
||||
|
@ -80,7 +81,7 @@
|
|||
seen.push(item)
|
||||
}
|
||||
}
|
||||
return Utils.NoNull(seen).filter((i) => !knownIds.has(i.id))
|
||||
return Lists.noNull(seen).filter((i) => !knownIds.has(i.id))
|
||||
})
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
import type { Feature } from "geojson"
|
||||
import { onDestroy } from "svelte"
|
||||
import WikidataValidator from "../Validators/WikidataValidator"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
export let args: (string | number | boolean)[] = []
|
||||
export let feature: Feature
|
||||
|
@ -43,12 +44,8 @@
|
|||
})
|
||||
)
|
||||
|
||||
let instanceOf: number[] = Utils.NoNull(
|
||||
(options?.instanceOf ?? []).map((i) => Wikidata.QIdToNumber(i))
|
||||
)
|
||||
let notInstanceOf: number[] = Utils.NoNull(
|
||||
(options?.notInstanceOf ?? []).map((i) => Wikidata.QIdToNumber(i))
|
||||
)
|
||||
let instanceOf: number[] = Lists.noNull((options?.instanceOf ?? []).map((i) => Wikidata.QIdToNumber(i)))
|
||||
let notInstanceOf: number[] = Lists.noNull((options?.notInstanceOf ?? []).map((i) => Wikidata.QIdToNumber(i)))
|
||||
|
||||
let allowMultipleArg = options?.["multiple"] ?? "yes"
|
||||
let allowMultiple = allowMultipleArg === "yes" || "" + allowMultipleArg === "true"
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
},
|
||||
[Stores.chronic(5 * 60 * 1000)]
|
||||
)
|
||||
.mapD((date) => Utils.TwoDigits(date.getHours()) + ":" + Utils.TwoDigits(date.getMinutes()))
|
||||
.mapD((date) => Utils.twoDigits(date.getHours()) + ":" + Utils.twoDigits(date.getMinutes()))
|
||||
|
||||
let size = nextChange.map((change) =>
|
||||
change === undefined ? "absolute h-7 w-7" : "absolute h-5 w-5 top-0 left-1/4"
|
||||
|
|
|
@ -40,7 +40,7 @@ export class OH {
|
|||
if (h == 24) {
|
||||
return "00:00"
|
||||
}
|
||||
return Utils.TwoDigits(h) + ":" + Utils.TwoDigits(m)
|
||||
return Utils.twoDigits(h) + ":" + Utils.twoDigits(m)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -66,9 +66,7 @@
|
|||
const feature = state.indexedFeatures.featuresById.data.get(targetFeatureId)
|
||||
const featureTags = state.featureProperties.getStore(targetFeatureId)
|
||||
const rendering = tagRenderingConfig.GetRenderValue(featureTags.data).txt
|
||||
const specialRenderings = Utils.NoNull(
|
||||
SpecialVisualizations.constructSpecification(rendering)
|
||||
).filter((v) => typeof v !== "string" && v.func["supportsAutoAction"] === true)
|
||||
const specialRenderings = Lists.noNull(SpecialVisualizations.constructSpecification(rendering)).filter((v) => typeof v !== "string" && v.func["supportsAutoAction"] === true)
|
||||
|
||||
if (specialRenderings.length == 0) {
|
||||
console.warn(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import TagRenderingConfig from "../../../Models/ThemeConfig/TagRenderingConfig"
|
||||
import { Utils } from "../../../Utils"
|
||||
import { Translation } from "../../i18n/Translation"
|
||||
import TagRenderingMapping from "./TagRenderingMapping.svelte"
|
||||
import type { SpecialVisualizationState } from "../../SpecialVisualization"
|
||||
|
@ -9,6 +8,8 @@
|
|||
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { onDestroy } from "svelte"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
|
||||
export let tags: UIEventSource<Record<string, string> | undefined>
|
||||
|
||||
|
@ -27,7 +28,7 @@
|
|||
throw "Config is undefined in tagRenderingAnswer"
|
||||
}
|
||||
let trs: Store<{ then: Translation; icon?: string; iconClass?: string }[]> = tags.mapD((tags) =>
|
||||
Utils.NoNull(config?.GetRenderValues(tags)),onDestroy
|
||||
Lists.noNull(config?.GetRenderValues(tags)),onDestroy
|
||||
)
|
||||
</script>
|
||||
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
import { And } from "../../../Logic/Tags/And"
|
||||
import { get } from "svelte/store"
|
||||
import Markdown from "../../Base/Markdown.svelte"
|
||||
import { Utils } from "../../../Utils"
|
||||
import type { UploadableTag } from "../../../Logic/Tags/TagTypes"
|
||||
import { TagTypes } from "../../../Logic/Tags/TagTypes"
|
||||
|
||||
|
@ -36,6 +35,7 @@
|
|||
import If from "../../Base/If.svelte"
|
||||
import DotMenu from "../../Base/DotMenu.svelte"
|
||||
import SidebarUnit from "../../Base/SidebarUnit.svelte"
|
||||
import { Lists } from "../../../Utils/Lists"
|
||||
|
||||
export let config: TagRenderingConfig
|
||||
export let tags: UIEventSource<Record<string, string>>
|
||||
|
@ -152,7 +152,7 @@
|
|||
feedback.setData(undefined)
|
||||
}
|
||||
|
||||
let usedKeys: string[] = Utils.Dedup(config.usedTags().flatMap((t) => t.usedKeys()))
|
||||
let usedKeys: string[] = Lists.dedup(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.
|
||||
|
@ -342,7 +342,7 @@
|
|||
let menuIsOpened = new UIEventSource(false)
|
||||
|
||||
function disableQuestion() {
|
||||
const newList = Utils.Dedup([config.id, ...disabledInTheme.data])
|
||||
const newList = Lists.dedup([config.id, ...disabledInTheme.data])
|
||||
disabledInTheme.set(newList)
|
||||
menuIsOpened.set(false)
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
signature: string,
|
||||
madeByLoggedInUser: Store<boolean>
|
||||
})[]
|
||||
> = reviews.reviews.map((r) => Utils.NoNull(r))
|
||||
> = reviews.reviews.map((r) => Lists.noNull(r))
|
||||
let test = state.featureSwitches.featureSwitchIsTesting
|
||||
let debug = state.featureSwitches.featureSwitchIsDebugging
|
||||
let subject: Store<string> = reviews.subjectUri
|
||||
|
|
|
@ -6,22 +6,19 @@
|
|||
import ThemeSearch from "../../Logic/Search/ThemeSearch"
|
||||
import SidebarUnit from "../Base/SidebarUnit.svelte"
|
||||
import ThemeResult from "./ThemeResult.svelte"
|
||||
import type { SpecialVisualizationState } from "../SpecialVisualization"
|
||||
import DotMenu from "../Base/DotMenu.svelte"
|
||||
import { TrashIcon } from "@babeard/svelte-heroicons/mini"
|
||||
import { CogIcon } from "@rgossiaux/svelte-heroicons/solid"
|
||||
import Tr from "../Base/Tr.svelte"
|
||||
import { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
|
||||
import ThemeViewState from "../../Models/ThemeViewState"
|
||||
import { WithSearchState } from "../../Models/ThemeViewState/WithSearchState"
|
||||
import { Store } from "../../Logic/UIEventSource"
|
||||
import { Utils } from "../../Utils"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export let state: WithSearchState
|
||||
let searchTerm = state.searchState.searchTerm
|
||||
let visitedThemes = state.userRelatedState.recentlyVisitedThemes.value
|
||||
let recentThemes: Store<string[]> = visitedThemes.map((themes) =>
|
||||
Utils.Dedup(themes.filter((th) => th !== state.theme.id)).slice(0, 6))
|
||||
Lists.dedup(themes.filter((th) => th !== state.theme.id)).slice(0, 6))
|
||||
let themeResults = state.searchState.themeSuggestions
|
||||
|
||||
const t = Translations.t.general.search
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import {
|
||||
SpecialVisualization,
|
||||
SpecialVisualizationState,
|
||||
SpecialVisualizationSvelte,
|
||||
} from "../SpecialVisualization"
|
||||
import { SpecialVisualization, SpecialVisualizationState, SpecialVisualizationSvelte } from "../SpecialVisualization"
|
||||
import { ImmutableStore, Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import BaseUIElement from "../BaseUIElement"
|
||||
import SvelteUIElement from "../Base/SvelteUIElement"
|
||||
|
@ -16,6 +12,7 @@ import { Translation } from "../i18n/Translation"
|
|||
import { MapillaryLinkVis } from "../Popup/MapillaryLinkVis"
|
||||
import SendEmail from "../Popup/SendEmail.svelte"
|
||||
import DynLink from "../Base/DynLink.svelte"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
class FediverseLinkVis extends SpecialVisualization {
|
||||
funcName = "fediverse_link"
|
||||
|
@ -88,9 +85,7 @@ class WikidatalabelVis extends SpecialVisualization {
|
|||
const id = tagsSource
|
||||
.map((tags) => tags[args[0]])
|
||||
.map((wikidata) => {
|
||||
const wikidataIds = Utils.NoEmpty(
|
||||
wikidata?.split(";")?.map((wd) => wd.trim()) ?? []
|
||||
)
|
||||
const wikidataIds = Lists.noEmpty(wikidata?.split(";")?.map((wd) => wd.trim()) ?? [])
|
||||
return wikidataIds?.[0]
|
||||
})
|
||||
const entry = id.bind((id) => Wikidata.LoadWikidataEntry(id))
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
.filter((tr) => tr.question !== undefined)
|
||||
|
||||
let diffInDays = overview.mapD((overview) => {
|
||||
const dateStrings = Utils.NoNull(overview._meta.map((cs) => cs.properties.date))
|
||||
const dateStrings = Lists.noNull(overview._meta.map((cs) => cs.properties.date))
|
||||
const dates: number[] = dateStrings.map((d) => new Date(d).getTime())
|
||||
const mindate = Math.min(...dates)
|
||||
const maxdate = Math.max(...dates)
|
||||
|
|
|
@ -82,7 +82,7 @@ export class ChangesetsOverview {
|
|||
public readonly _meta: (ChangeSetData & OsmFeature)[]
|
||||
|
||||
private constructor(meta: (ChangeSetData & OsmFeature)[]) {
|
||||
this._meta = Utils.NoNull(meta)
|
||||
this._meta = Lists.noNull(meta)
|
||||
}
|
||||
|
||||
public static fromDirtyData(meta: (ChangeSetData & OsmFeature)[]): ChangesetsOverview {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import Loading from "../Base/Loading.svelte"
|
||||
import Checkbox from "../Base/Checkbox.svelte"
|
||||
import PlantNet from "../../Logic/Web/PlantNet"
|
||||
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
let services: MCService[] = []
|
||||
|
||||
let recheckSignal: UIEventSource<any> = new UIEventSource<any>(undefined)
|
||||
|
@ -345,7 +345,7 @@
|
|||
let someLoading = new UIEventSource(true)
|
||||
|
||||
function setAll() {
|
||||
const data = Utils.NoNull(services.map((s) => s.status.data))
|
||||
const data = Lists.noNull(services.map((s) => s.status.data))
|
||||
someLoading.setData(data.length !== services.length)
|
||||
if (data.some((d) => d === "offline")) {
|
||||
all.setData("offline")
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
import DeleteButton from "./DeleteButton.svelte"
|
||||
import StudioHashSetter from "./StudioHashSetter"
|
||||
import TitledPanel from "../Base/TitledPanel.svelte"
|
||||
import Popup from "../Base/Popup.svelte"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
|||
)
|
||||
const configuration = state.configuration
|
||||
|
||||
const allNames = Utils.Dedup(layerSchema.map((meta) => meta.hints.group))
|
||||
const allNames = Lists.dedup(layerSchema.map((meta) => meta.hints.group))
|
||||
|
||||
const perRegion: Record<string, ConfigMeta[]> = {}
|
||||
for (const region of allNames) {
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
import { ConfigMeta } from "./configMeta"
|
||||
import { Store, UIEventSource } from "../../Logic/UIEventSource"
|
||||
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
|
||||
import {
|
||||
Conversion,
|
||||
ConversionMessage,
|
||||
DesugaringContext,
|
||||
Pipe,
|
||||
} from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import { Conversion, ConversionMessage, DesugaringContext, Pipe } from "../../Models/ThemeConfig/Conversion/Conversion"
|
||||
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
|
||||
import { PrevalidateTheme, ValidateLayer } from "../../Models/ThemeConfig/Conversion/Validation"
|
||||
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
|
||||
|
@ -25,6 +20,7 @@ import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
|
|||
import { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
|
||||
import { ValidateTheme } from "../../Models/ThemeConfig/Conversion/ValidateTheme"
|
||||
import * as questions from "../../../public/assets/generated/layers/questions.json"
|
||||
import { Lists } from "../../Utils/Lists"
|
||||
|
||||
export interface HighlightedTagRendering {
|
||||
path: ReadonlyArray<string | number>
|
||||
|
@ -380,7 +376,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
if (data[key]) {
|
||||
// A bit of cleanup
|
||||
const lBefore = data[key].length
|
||||
const cleaned = Utils.NoNull(data[key])
|
||||
const cleaned = Lists.noNull(data[key])
|
||||
if (cleaned.length != lBefore) {
|
||||
data[key] = cleaned
|
||||
return true
|
||||
|
@ -422,7 +418,7 @@ export default class EditLayerState extends EditJsonState<LayerConfigJson> {
|
|||
|
||||
private addMissingTagRenderingIds() {
|
||||
this.configuration.addCallbackD((config) => {
|
||||
const trs = Utils.NoNull(config.tagRenderings ?? [])
|
||||
const trs = Lists.noNull(config.tagRenderings ?? [])
|
||||
for (let i = 0; i < trs.length; i++) {
|
||||
const tr = trs[i]
|
||||
if (typeof tr === "string") {
|
||||
|
@ -544,7 +540,7 @@ export class EditThemeState extends EditJsonState<ThemeConfigJson> {
|
|||
const prepare = this.buildValidation(state)
|
||||
const context = ConversionContext.construct([], ["prepare"])
|
||||
if (configuration.layers) {
|
||||
Utils.NoNullInplace(configuration.layers)
|
||||
Lists.noNullInplace(configuration.layers)
|
||||
}
|
||||
try {
|
||||
prepare.convert(<ThemeConfigJson>configuration, context)
|
||||
|
|
127
src/Utils.ts
127
src/Utils.ts
|
@ -1,4 +1,6 @@
|
|||
import DOMPurify from "dompurify"
|
||||
import { Lists } from "./Utils/Lists"
|
||||
import { Strings } from "./Utils/Strings"
|
||||
|
||||
export class Utils {
|
||||
/**
|
||||
|
@ -109,7 +111,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
return <T>parsed
|
||||
}
|
||||
|
||||
static EncodeXmlValue(str) {
|
||||
static encodeXmlValue(str) {
|
||||
if (typeof str !== "string") {
|
||||
str = "" + str
|
||||
}
|
||||
|
@ -122,26 +124,11 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
.replace(/'/g, "'")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gives a clean float, or undefined if parsing fails
|
||||
* @param str
|
||||
*/
|
||||
static asFloat(str): number {
|
||||
if (str) {
|
||||
const i = parseFloat(str)
|
||||
if (isNaN(i)) {
|
||||
return undefined
|
||||
}
|
||||
return i
|
||||
}
|
||||
return undefined
|
||||
public static upper(str: string) {
|
||||
return str.substring(0, 1).toUpperCase() + str.substring(1)
|
||||
}
|
||||
|
||||
public static Upper(str: string) {
|
||||
return str.substr(0, 1).toUpperCase() + str.substr(1)
|
||||
}
|
||||
|
||||
public static TwoDigits(i: number) {
|
||||
public static twoDigits(i: number) {
|
||||
if (i < 10) {
|
||||
return "0" + i
|
||||
}
|
||||
|
@ -151,16 +138,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
/**
|
||||
* Converts a number to a number with precisely 7 decimals
|
||||
*
|
||||
* Utils.Round7(12.123456789) // => 12.1234568
|
||||
* Utils.round7(12.123456789) // => 12.1234568
|
||||
*/
|
||||
public static Round7(i: number): number {
|
||||
public static round7(i: number): number {
|
||||
if (i == undefined) {
|
||||
return undefined
|
||||
}
|
||||
return Math.round(i * 10000000) / 10000000
|
||||
}
|
||||
|
||||
public static Times(f: (i: number) => string, count: number): string {
|
||||
public static times(f: (i: number) => string, count: number): string {
|
||||
let res = ""
|
||||
for (let i = 0; i < count; i++) {
|
||||
res += f(i)
|
||||
|
@ -168,7 +155,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
return res
|
||||
}
|
||||
|
||||
public static TimesT<T>(count: number, f: (i: number) => T): T[] {
|
||||
public static timesT<T>(count: number, f: (i: number) => T): T[] {
|
||||
const res: T[] = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
res.push(f(i))
|
||||
|
@ -176,16 +163,8 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of the array. All elements that are not undefined/null will be in the new list
|
||||
* @param array
|
||||
* @constructor
|
||||
*/
|
||||
public static NoNull<T>(array: ReadonlyArray<T> | undefined): T[] | undefined
|
||||
public static NoNull(array: undefined): undefined
|
||||
public static NoNull<T>(array: ReadonlyArray<T>): T[]
|
||||
public static NoNull<T>(array: ReadonlyArray<T>): NonNullable<T>[] {
|
||||
return <NonNullable<T>[]>(<unknown>array?.filter((o) => o !== undefined && o !== null))
|
||||
public static noNull<T>(array: ReadonlyArray<T>): NonNullable<T>[] {
|
||||
return Lists.noNull(array)
|
||||
}
|
||||
|
||||
public static Hist(array: ReadonlyArray<string>): Map<string, number> {
|
||||
|
@ -195,29 +174,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
return hist
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all empty strings from this list
|
||||
* If undefined or null is given, an empty list is returned
|
||||
*
|
||||
* Utils.NoEmpty(undefined) // => []
|
||||
* Utils.NoEmpty(["abc","","def", null]) // => ["abc","def", null]
|
||||
*
|
||||
*/
|
||||
public static NoEmpty(array: string[]): string[] {
|
||||
const ls: string[] = []
|
||||
if (!array) {
|
||||
return ls
|
||||
}
|
||||
for (const t of array) {
|
||||
if (t === "") {
|
||||
continue
|
||||
}
|
||||
ls.push(t)
|
||||
}
|
||||
return ls
|
||||
}
|
||||
|
||||
public static EllipsesAfter(str: string, l: number = 100) {
|
||||
if (str === undefined || str === null) {
|
||||
return undefined
|
||||
|
@ -297,28 +253,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
return str
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array with all elements from 'arr' in such a way that every element will be kept only once
|
||||
* Elements are returned in the same order as they appear in the lists.
|
||||
* Null/Undefined is returned as is. If an emtpy array is given, a new empty array will be returned
|
||||
*/
|
||||
public static Dedup(arr: NonNullable<string[]>): NonNullable<string[]>
|
||||
public static Dedup(arr: undefined): undefined
|
||||
public static Dedup(arr: string[] | undefined): string[] | undefined
|
||||
public static Dedup(arr: string[]): string[] {
|
||||
if (arr === undefined || arr === null) {
|
||||
return arr
|
||||
}
|
||||
const newArr = []
|
||||
for (const string of arr) {
|
||||
if (newArr.indexOf(string) < 0) {
|
||||
newArr.push(string)
|
||||
}
|
||||
}
|
||||
return newArr
|
||||
}
|
||||
|
||||
public static DedupT<T>(arr: T[]): T[] {
|
||||
if (!arr) {
|
||||
return arr
|
||||
|
@ -359,7 +293,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
uniq.push(img)
|
||||
}
|
||||
} else {
|
||||
const ksNoNull = Utils.NoNull(ks)
|
||||
const ksNoNull = Lists.noNull(ks)
|
||||
const hasBeenSeen = ksNoNull.some((k) => seen.has(k))
|
||||
if (!hasBeenSeen) {
|
||||
uniq.push(img)
|
||||
|
@ -1213,7 +1147,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
if (days > 0) {
|
||||
return days + "days" + " " + hours + "h"
|
||||
}
|
||||
return hours + ":" + Utils.TwoDigits(minutes) + ":" + Utils.TwoDigits(seconds)
|
||||
return hours + ":" + Utils.twoDigits(minutes) + ":" + Utils.twoDigits(seconds)
|
||||
}
|
||||
|
||||
public static toHumanByteSize(bytes: number): string {
|
||||
|
@ -1247,9 +1181,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
const date =
|
||||
lastWeek.getFullYear() +
|
||||
"-" +
|
||||
Utils.TwoDigits(lastWeek.getMonth() + 1) +
|
||||
Utils.twoDigits(lastWeek.getMonth() + 1) +
|
||||
"-" +
|
||||
Utils.TwoDigits(lastWeek.getDate())
|
||||
Utils.twoDigits(lastWeek.getDate())
|
||||
let osmcha_link = `"date__gte":[{"label":"${date}","value":"${date}"}],"editor":[{"label":"mapcomplete","value":"mapcomplete"}]`
|
||||
if (theme !== undefined) {
|
||||
osmcha_link =
|
||||
|
@ -1833,16 +1767,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
}
|
||||
return 50 * Math.round(number / 50)
|
||||
}
|
||||
|
||||
public static NoNullInplace<T>(items: T[]): T[] {
|
||||
for (let i = items.length - 1; i >= 0; i--) {
|
||||
if (items[i] === null || items[i] === undefined || items[i] === "") {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes or rewrites some characters in links, as some blink/chromium based browsers are picky about them
|
||||
*
|
||||
|
@ -1860,8 +1784,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
return href
|
||||
}
|
||||
|
||||
private static emojiRegex = /[\p{Extended_Pictographic}🛰️]/u
|
||||
|
||||
/**
|
||||
* Returns 'true' if the given string contains at least one and only emoji characters
|
||||
*
|
||||
|
@ -1870,19 +1792,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
* Utils.isEmoji("🍕") // => true
|
||||
*/
|
||||
public static isEmoji(string: string) {
|
||||
return Utils.emojiRegex.test(string) || Utils.isEmojiFlag(string)
|
||||
return Strings.isEmoji(string)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utils.isEmojiFlag("🇧🇪") // => true
|
||||
*/
|
||||
public static isEmojiFlag(string: string) {
|
||||
return /[🇦-🇿]{2}/u.test(string) // flags, see https://stackoverflow.com/questions/53360006/detect-with-regex-if-emoji-is-country-flag
|
||||
}
|
||||
|
||||
public static concat<T>(param: T[][]): T[] {
|
||||
return [].concat(...param)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -1905,9 +1817,4 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
|
|||
return <T>copy
|
||||
}
|
||||
|
||||
public static pass(){
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
89
src/Utils/Lists.ts
Normal file
89
src/Utils/Lists.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* Various utils to manipulate lists
|
||||
*/
|
||||
export class Lists {
|
||||
/**
|
||||
* Creates a shallow copy of the array. All elements that are not undefined/null will be in the new list
|
||||
*/
|
||||
public static noNull<T>(array: ReadonlyArray<T> | undefined): T[] | undefined
|
||||
public static noNull(array: undefined): undefined
|
||||
public static noNull<T>(array: ReadonlyArray<T>): T[]
|
||||
public static noNull<T>(array: ReadonlyArray<T>): NonNullable<T>[] {
|
||||
return <NonNullable<T>[]>(<unknown>array?.filter((o) => o !== undefined && o !== null))
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all empty strings from this list
|
||||
* If undefined or null is given, an empty list is returned
|
||||
*
|
||||
* Lists.noEmpty(undefined) // => []
|
||||
* Lists.noEmpty(["abc","","def", null]) // => ["abc","def", null]
|
||||
*
|
||||
*/
|
||||
public static noEmpty(array: undefined | null): string[]
|
||||
public static noEmpty(array: undefined[]): undefined[]
|
||||
public static noEmpty(array: (string | "")[]): Exclude<string, "">[]
|
||||
public static noEmpty(array: (string | undefined | null | "")[]): (string | undefined | null)[]
|
||||
public static noEmpty(array: string[]): string[] {
|
||||
const ls: string[] = []
|
||||
if (!array) {
|
||||
return ls
|
||||
}
|
||||
for (const t of array) {
|
||||
if (t === "") {
|
||||
continue
|
||||
}
|
||||
ls.push(t)
|
||||
}
|
||||
return ls
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new array with all elements from 'arr' in such a way that every element will be kept only once
|
||||
* Elements are returned in the same order as they appear in the lists.
|
||||
* Null/Undefined is returned as is. If an empty array is given, a new empty array will be returned
|
||||
*/
|
||||
public static dedup(arr: NonNullable<string[]>): NonNullable<string[]>
|
||||
public static dedup(arr: undefined): undefined
|
||||
public static dedup(arr: string[] | undefined): string[] | undefined
|
||||
public static dedup(arr: string[]): string[] {
|
||||
if (arr === undefined || arr === null) {
|
||||
return arr
|
||||
}
|
||||
const newArr = []
|
||||
for (const string of arr) {
|
||||
if (newArr.indexOf(string) < 0) {
|
||||
newArr.push(string)
|
||||
}
|
||||
}
|
||||
return newArr
|
||||
}
|
||||
|
||||
public static dedupT<T>(arr: ReadonlyArray<T>): T[] ;
|
||||
public static dedupT<T>(arr: null): null ;
|
||||
public static dedupT<T>(arr: undefined): undefined ;
|
||||
public static dedupT<T>(arr: ReadonlyArray<T>): T[] {
|
||||
if (arr === undefined) {
|
||||
return undefined
|
||||
}
|
||||
if (arr === null) {
|
||||
return null
|
||||
}
|
||||
const items = []
|
||||
for (const item of arr) {
|
||||
if (items.indexOf(item) < 0) {
|
||||
items.push(item)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
public static noNullInplace<T>(items: T[]): T[] {
|
||||
for (let i = items.length - 1; i >= 0; i--) {
|
||||
if (items[i] === null || items[i] === undefined || items[i] === "") {
|
||||
items.splice(i, 1)
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
25
src/Utils/Strings.ts
Normal file
25
src/Utils/Strings.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
* Various string-related utils
|
||||
*/
|
||||
export class Strings{
|
||||
private static emojiRegex = /[\p{Extended_Pictographic}🛰️]/u
|
||||
|
||||
/**
|
||||
* Returns 'true' if the given string contains at least one and only emoji characters
|
||||
*
|
||||
* Strings.isEmoji("⛰\uFE0F") // => true
|
||||
* Strings.isEmoji("🇧🇪") // => true
|
||||
* Strings.isEmoji("🍕") // => true
|
||||
*/
|
||||
public static isEmoji(string: string): boolean {
|
||||
return Strings.emojiRegex.test(string) || Strings.isEmojiFlag(string)
|
||||
}
|
||||
|
||||
/**
|
||||
* Strings.isEmojiFlag("🇧🇪") // => true
|
||||
*/
|
||||
public static isEmojiFlag(string: string): boolean {
|
||||
return /[🇦-🇿]{2}/u.test(string) // flags, see https://stackoverflow.com/questions/53360006/detect-with-regex-if-emoji-is-country-flag
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue