diff --git a/src/UI/BigComponents/MoreScreen.ts b/src/UI/BigComponents/MoreScreen.ts index 045bf3ec5..e280d577c 100644 --- a/src/UI/BigComponents/MoreScreen.ts +++ b/src/UI/BigComponents/MoreScreen.ts @@ -118,7 +118,7 @@ export default class MoreScreen extends Combine { if (search === undefined) { return true } - search = search.toLocaleLowerCase() + search = Utils.RemoveDiacritics(search.toLocaleLowerCase()) if (search.length > 3 && layout.id.toLowerCase().indexOf(search) >= 0) { return true } @@ -131,7 +131,7 @@ export default class MoreScreen extends Combine { continue } const term = entity["*"] ?? entity[Locale.language.data] - if (term?.toLowerCase()?.indexOf(search) >= 0) { + if (Utils.RemoveDiacritics(term?.toLowerCase())?.indexOf(search) >= 0) { return true } } diff --git a/src/Utils.ts b/src/Utils.ts index c89e4a468..fc834a06d 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -11,7 +11,7 @@ export class Utils { public static readonly assets_path = "./assets/svg/" public static externalDownloadFunction: ( url: string, - headers?: any + headers?: any, ) => Promise<{ content: string } | { redirect: string }> public static Special_visualizations_tagsToApplyHelpText = `These can either be a tag to add, such as \`amenity=fast_food\` or can use a substitution, e.g. \`addr:housenumber=$number\`. This new point will then have the tags \`amenity=fast_food\` and \`addr:housenumber\` with the value that was saved in \`number\` in the original feature. @@ -150,7 +150,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (Utils.runningFromConsole) { return } - DOMPurify.addHook("afterSanitizeAttributes", function (node) { + DOMPurify.addHook("afterSanitizeAttributes", function(node) { // set all elements owning target to target=_blank + add noopener noreferrer const target = node.getAttribute("target") if (target) { @@ -172,7 +172,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be */ public static ParseVisArgs( specs: { name: string; defaultValue?: string }[], - args: string[] + args: string[], ): Record { const parsed: Record = {} if (args.length > specs.length) { @@ -320,7 +320,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be object: any, name: string, init: () => any, - whenDone?: () => void + whenDone?: () => void, ) { Object.defineProperty(object, name, { enumerable: false, @@ -343,7 +343,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be object: any, name: string, init: () => Promise, - whenDone?: () => void + whenDone?: () => void, ) { Object.defineProperty(object, name, { enumerable: false, @@ -483,7 +483,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be public static SubstituteKeys( txt: string | undefined, tags: Record | undefined, - useLang?: string + useLang?: string, ): string | undefined { if (txt === undefined) { return undefined @@ -519,7 +519,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be "SubstituteKeys received a BaseUIElement to substitute in - this is probably a bug and will be downcast to a string\nThe key is", key, "\nThe value is", - v + v, ) v = v.InnerConstructElement()?.textContent } @@ -561,38 +561,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be target.push(...source) } - /** - * Recursively rewrites all keys from `+key`, `key+` and `=key` into `key - * - * Utils.CleanMergeObject({"condition":{"and+":["xyz"]}} // => {"condition":{"and":["xyz"]}} - * @param obj - * @constructor - * @private - */ - private static CleanMergeObject(obj: any) { - if (Array.isArray(obj)) { - const result = [] - for (const el of obj) { - result.push(Utils.CleanMergeObject(el)) - } - return result - } - if (typeof obj !== "object") { - return obj - } - const newObj = {} - for (let objKey in obj) { - let cleanKey = objKey - if (objKey.startsWith("+") || objKey.startsWith("=")) { - cleanKey = objKey.substring(1) - } else if (objKey.endsWith("+") || objKey.endsWith("=")) { - cleanKey = objKey.substring(0, objKey.length - 1) - } - newObj[cleanKey] = Utils.CleanMergeObject(obj[objKey]) - } - return newObj - } - /** * Copies all key-value pairs of the source into the target. This will change the target * If the key starts with a '+', the values of the list will be appended to the target instead of overwritten @@ -664,7 +632,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (!Array.isArray(targetV)) { throw new Error( "Cannot concatenate: value to add is not an array: " + - JSON.stringify(targetV) + JSON.stringify(targetV), ) } if (Array.isArray(sourceV)) { @@ -672,9 +640,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } else { throw new Error( "Could not merge concatenate " + - JSON.stringify(sourceV) + - " and " + - JSON.stringify(targetV) + JSON.stringify(sourceV) + + " and " + + JSON.stringify(targetV), ) } } else { @@ -719,7 +687,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be path: string[], object: any, replaceLeaf: (leaf: any, travelledPath: string[]) => any, - travelledPath: string[] = [] + travelledPath: string[] = [], ): void { if (object == null) { return @@ -750,7 +718,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } if (Array.isArray(sub)) { sub.forEach((el, i) => - Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, "" + i]) + Utils.WalkPath(path.slice(1), el, replaceLeaf, [...travelledPath, head, "" + i]), ) return } @@ -767,7 +735,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be path: string[], object: any, collectedList: { leaf: any; path: string[] }[] = [], - travelledPath: string[] = [] + travelledPath: string[] = [], ): { leaf: any; path: string[] }[] { if (object === undefined || object === null) { return collectedList @@ -797,7 +765,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be if (Array.isArray(sub)) { sub.forEach((el, i) => - Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath, "" + i]) + Utils.CollectPath(path.slice(1), el, collectedList, [...travelledPath, "" + i]), ) return collectedList } @@ -841,7 +809,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be json: any, f: (v: object | number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, - path: string[] = [] + path: string[] = [], ) { if (json === undefined || json === null) { return f(json, path) @@ -880,7 +848,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be json: any, collect: (v: number | string | boolean | undefined, path: string[]) => any, isLeaf: (object) => boolean = undefined, - path = [] + path = [], ): void { if (json === undefined) { return @@ -955,7 +923,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be continue } const i = part.charCodeAt(0) - result += '"' + keys[i] + '":' + part.substring(1) + result += "\"" + keys[i] + "\":" + part.substring(1) } return result @@ -982,7 +950,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be url: string, headers?: any, method: "POST" | "GET" | "PUT" | "UPDATE" | "DELETE" | "OPTIONS" = "GET", - content?: string + content?: string, ): Promise< | { content: string } | { redirect: string } @@ -1047,7 +1015,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be public static async downloadJsonCached( url: string, maxCacheTimeMs: number, - headers?: any + headers?: any, ): Promise { const result = await Utils.downloadJsonAdvanced(url, headers) if (result["content"]) { @@ -1059,7 +1027,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be public static async downloadJsonCachedAdvanced( url: string, maxCacheTimeMs: number, - headers?: any + headers?: any, ): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> { const cached = Utils._download_cache.get(url) if (cached !== undefined) { @@ -1069,9 +1037,9 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } const promise = /*NO AWAIT as we work with the promise directly */ Utils.downloadJsonAdvanced( - url, - headers - ) + url, + headers, + ) Utils._download_cache.set(url, { promise, timestamp: new Date().getTime() }) return await promise } @@ -1086,7 +1054,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be public static async downloadJsonAdvanced( url: string, - headers?: any + headers?: any, ): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> { const injected = Utils.injectedDownloads[url] if (injected !== undefined) { @@ -1095,7 +1063,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } const result = await Utils.downloadAdvanced( url, - Utils.Merge({ accept: "application/json" }, headers ?? {}) + Utils.Merge({ accept: "application/json" }, headers ?? {}), ) if (result["error"] !== undefined) { return <{ error: string; url: string; statuscode?: number }>result @@ -1115,7 +1083,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be "due to", e, "\n", - e.stack + e.stack, ) return { error: "malformed", url } } @@ -1136,7 +1104,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | "{gpx=application/gpx+xml}" | "application/json" | "image/png" - } + }, ) { const element = document.createElement("a") let file @@ -1240,7 +1208,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be e.preventDefault() return false }, - false + false, ) } @@ -1324,7 +1292,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be public static sortedByLevenshteinDistance( reference: string, ts: T[], - getName: (t: T) => string + getName: (t: T) => string, ): T[] { const withDistance: [T, number][] = ts.map((t) => [ t, @@ -1350,7 +1318,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be track[j][i] = Math.min( track[j][i - 1] + 1, // deletion track[j - 1][i] + 1, // insertion - track[j - 1][i - 1] + indicator // substitution + track[j - 1][i - 1] + indicator, // substitution ) } } @@ -1359,7 +1327,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be public static MapToObj( d: Map, - onValue: (t: V, key: string) => T + onValue: (t: V, key: string) => T, ): Record { const o = {} const keys = Array.from(d.keys()) @@ -1376,7 +1344,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * Utils.TransposeMap({"a" : ["b", "c"], "x" : ["b", "y"]}) // => {"b" : ["a", "x"], "c" : ["a"], "y" : ["x"]} */ public static TransposeMap( - d: Record + d: Record, ): Record { const newD: Record = {} @@ -1450,7 +1418,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } public static asDict( - tags: { key: string; value: string | number }[] + tags: { key: string; value: string | number }[], ): Map { const d = new Map() @@ -1491,21 +1459,6 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be element.scrollIntoView({ behavior: "smooth", block: "nearest" }) } - private static findParentWithScrolling(element: HTMLBaseElement): HTMLBaseElement { - // Check if the element itself has scrolling - if (element.scrollHeight > element.clientHeight) { - return element - } - - // If the element does not have scrolling, check if it has a parent element - if (!element.parentElement) { - return null - } - - // If the element has a parent, repeat the process for the parent element - return Utils.findParentWithScrolling(element.parentElement) - } - /** * Returns true if the contents of `a` are the same (and in the same order) as `b`. * Might have false negatives in some cases @@ -1572,7 +1525,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be * */ public static splitIntoSubstitutionParts( - template: string + template: string, ): ({ message: string } | { subs: string })[] { const preparts = template.split("{") const spec: ({ message: string } | { subs: string })[] = [] @@ -1633,6 +1586,13 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } } + public static RemoveDiacritics(str?: string): string { + if(!str){ + return str + } + return str.normalize("NFD").replace(/\p{Diacritic}/gu, "") + } + public static randomString(length: number): string { let result = "" for (let i = 0; i < length; i++) { @@ -1641,9 +1601,57 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be } return result } + + /** + * Recursively rewrites all keys from `+key`, `key+` and `=key` into `key + * + * Utils.CleanMergeObject({"condition":{"and+":["xyz"]}} // => {"condition":{"and":["xyz"]}} + * @param obj + * @constructor + * @private + */ + private static CleanMergeObject(obj: any) { + if (Array.isArray(obj)) { + const result = [] + for (const el of obj) { + result.push(Utils.CleanMergeObject(el)) + } + return result + } + if (typeof obj !== "object") { + return obj + } + const newObj = {} + for (let objKey in obj) { + let cleanKey = objKey + if (objKey.startsWith("+") || objKey.startsWith("=")) { + cleanKey = objKey.substring(1) + } else if (objKey.endsWith("+") || objKey.endsWith("=")) { + cleanKey = objKey.substring(0, objKey.length - 1) + } + newObj[cleanKey] = Utils.CleanMergeObject(obj[objKey]) + } + return newObj + } + + private static findParentWithScrolling(element: HTMLBaseElement): HTMLBaseElement { + // Check if the element itself has scrolling + if (element.scrollHeight > element.clientHeight) { + return element + } + + // If the element does not have scrolling, check if it has a parent element + if (!element.parentElement) { + return null + } + + // If the element has a parent, repeat the process for the parent element + return Utils.findParentWithScrolling(element.parentElement) + } + private static colorDiff( c0: { r: number; g: number; b: number }, - c1: { r: number; g: number; b: number } + c1: { r: number; g: number; b: number }, ) { return Math.abs(c0.r - c1.r) + Math.abs(c0.g - c1.g) + Math.abs(c0.b - c1.b) }