diff --git a/Logic/ImageSearcher.ts b/Logic/ImageSearcher.ts index b210c254f..79b88c9ff 100644 --- a/Logic/ImageSearcher.ts +++ b/Logic/ImageSearcher.ts @@ -130,7 +130,12 @@ export class ImageSearcher extends UIEventSource<{key: string, url: string}[]> { } if (this._tags.data.mapillary) { - this.AddImage(undefined,"https://www.mapillary.com/map/im/" + this._tags.data.mapillary) + let mapillary = this._tags.data.mapillary; + const prefix = "https://www.mapillary.com/map/im/"; + if(mapillary.indexOf(prefix) < 0){ + mapillary = prefix + mapillary; + } + this.AddImage(undefined, mapillary) } } diff --git a/Logic/Tags.ts b/Logic/Tags.ts index fafd17046..634770abe 100644 --- a/Logic/Tags.ts +++ b/Logic/Tags.ts @@ -1,4 +1,6 @@ import {Utils} from "../Utils"; +import {Util} from "leaflet"; +import indexOf = Util.indexOf; export abstract class TagsFilter { abstract matches(tags: { k: string, v: string }[]): boolean @@ -344,20 +346,31 @@ export class TagUtils { } /** - * Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set. - * E.g: - * - * FlattenMultiAnswer([and: [ "x=a", "y=0;1"], and: ["x=b", "y=2"], and: ["x=", "y=3"]]) - * will result in - * ["x=a;b", "y=0;1;2;3"] - * + * Given two hashes of {key --> values[]}, makes sure that every neededTag is present in availableTags + */ + static AllKeysAreContained(availableTags: any, neededTags: any){ + for (const neededKey in neededTags) { + const availableValues : string[] = availableTags[neededKey] + if(availableValues === undefined){ + return false; + } + const neededValues : string[] = neededTags[neededKey]; + for (const neededValue of neededValues) { + if(indexOf(availableValues, neededValue) < 0){ + return false; + } + } + } + return true; + } + + /*** + * Creates a hash {key --> [values]}, with all the values present in the tagsfilter + * * @param tagsFilters * @constructor */ - static FlattenMultiAnswer(tagsFilters: TagsFilter[]): And { - if (tagsFilters === undefined) { - return new And([]); - } + static SplitKeys(tagsFilters: TagsFilter[]){ const keyValues = {} // Map string -> string[] tagsFilters = [...tagsFilters] // copy all while (tagsFilters.length > 0) { @@ -384,91 +397,30 @@ export class TagUtils { console.error("Invalid type to flatten the multiAnswer", tagsFilter); throw "Invalid type to FlattenMultiAnswer" } + return keyValues; + } + /** + * Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set. + * E.g: + * + * FlattenMultiAnswer([and: [ "x=a", "y=0;1"], and: ["x=b", "y=2"], and: ["x=", "y=3"]]) + * will result in + * ["x=a;b", "y=0;1;2;3"] + * + * @param tagsFilters + * @constructor + */ + static FlattenMultiAnswer(tagsFilters: TagsFilter[]): And { + if (tagsFilters === undefined) { + return new And([]); + } + let keyValues = TagUtils.SplitKeys(tagsFilters); const and: TagsFilter[] = [] for (const key in keyValues) { and.push(new Tag(key, Utils.Dedup(keyValues[key]).join(";"))); } return new And(and); - } - /** - * Splits the actualTags onto a list of which the values are the same as the tagsFilters. - * Leftovers are returned in the list too if there is an 'undefined' value - */ - static SplitMultiAnswer(actualTags: TagsFilter, possibleTags: TagsFilter[], freeformKey: string, freeformExtraTags: TagsFilter): TagsFilter[] { - - const queue: TagsFilter[] = [actualTags] - - const keyValues = {} // key ==> value[] - - while (queue.length > 0) { - const tf = queue.pop(); - if (tf instanceof And) { - queue.push(...tf.and); - continue; - } - if (tf instanceof Tag) { - if (keyValues[tf.key] === undefined) { - keyValues[tf.key] = [] - } - keyValues[tf.key].push(...tf.value.split(";")); - continue; - } - - if (tf === undefined) { - continue; - } - - throw "Invalid tagfilter: " + JSON.stringify(tf) - } - - const foundValues = []; - for (const possibleTag of possibleTags) { - if (possibleTag === undefined) { - continue; - } - if (possibleTag instanceof Tag) { - const key = possibleTag.key; - const actualValues: string[] = keyValues[key] ?? []; - const possibleValues = possibleTag.value.split(";"); - - let allPossibleValuesFound = true; - for (const possibleValue of possibleValues) { - if (actualValues.indexOf(possibleValue) < 0) { - allPossibleValuesFound = false; - } - } - if (!allPossibleValuesFound) { - continue; - } - - // At this point, we know that 'possibleTag' is completely present in the tagset - // we add the possibleTag to the found values - foundValues.push(possibleTag); - - for (const possibleValue of possibleValues) { - actualValues.splice(actualValues.indexOf(possibleValue), 1); - } - - continue; - } - throw "Unsupported possibletag: " + JSON.stringify(possibleTag); - } - - let leftoverTag = undefined; - if (keyValues[freeformKey] !== undefined && keyValues[freeformKey].length !== 0) { - leftoverTag = new Tag(freeformKey, keyValues[freeformKey].join(";")); - if (freeformExtraTags !== undefined) { - leftoverTag = new And([ - leftoverTag, - freeformExtraTags - ]) - } - foundValues.push(leftoverTag); - } - - return foundValues; - } } diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts index 1dd6e44a0..2c01b9926 100644 --- a/UI/Popup/TagRenderingQuestion.ts +++ b/UI/Popup/TagRenderingQuestion.ts @@ -18,6 +18,8 @@ import {Changes} from "../../Logic/Osm/Changes"; import {VariableUiElement} from "../Base/VariableUIElement"; import Translations from "../i18n/Translations"; import {FixedUiElement} from "../Base/FixedUiElement"; +import {Util} from "leaflet"; +import indexOf = Util.indexOf; /** * Shows the question element. @@ -112,6 +114,10 @@ export default class TagRenderingQuestion extends UIElement { } + private SplitMultiAnswer(tags: TagsFilter) { + + } + private GenerateMultiAnswer(elements: InputElement[], freeformField: InputElement): InputElement { const possibleTags = elements.map(el => el.GetValue().data); const checkBoxes = new CheckBoxes(elements); @@ -128,22 +134,50 @@ export default class TagRenderingQuestion extends UIElement { return TagUtils.FlattenMultiAnswer(tags); }, (tags: TagsFilter) => { - const splitUpValues = TagUtils.SplitMultiAnswer(tags, possibleTags, this._configuration.freeform?.key, new And(this._configuration.freeform?.addExtraTags)); + // {key --> values[]} + const presentTags = TagUtils.SplitKeys([tags]); const indices: number[] = [] + // We also collect the values that have to be added to the freeform field + let freeformExtras: string[] = [] + if (this._configuration.freeform?.key) { + freeformExtras = [...(presentTags[this._configuration.freeform.key] ?? [])] + } - for (let i = 0; i < splitUpValues.length; i++) { - let splitUpValue = splitUpValues[i]; + for (let j = 0; j < elements.length; j++) { + const inputElement = elements[j]; + if (inputElement === freeformField) { + continue; + } + const val = inputElement.GetValue(); + const neededTags = TagUtils.SplitKeys([val.data]); - for (let j = 0; j < elements.length; j++) { - let inputElement = elements[j]; - if (inputElement.IsValid(splitUpValue)) { - indices.push(j); - inputElement.GetValue().setData(splitUpValue); - break; + // if every 'neededKeys'-value is present in presentKeys, we have a match and enable the index + if (TagUtils.AllKeysAreContained(presentTags, neededTags)) { + indices.push(j); + if (freeformExtras.length > 0) { + const freeformsToRemove: string[] = (neededTags[this._configuration.freeform.key] ?? []); + for (const toRm of freeformsToRemove) { + const i = freeformExtras.indexOf(toRm); + if (i >= 0) { + freeformExtras.splice(i, 1); + } + } } } + } - console.log(indices) + console.log(indices, freeformExtras); + + if (freeformField) { + if (freeformExtras.length > 0) { + freeformField.GetValue().setData(new Tag(this._configuration.freeform.key, freeformExtras.join(";"))); + indices.push(indexOf(elements, freeformField)) + } else { + freeformField.GetValue().setData(undefined); + } + } + + return indices; }, elements.map(el => el.GetValue()) diff --git a/css/tagrendering.css b/css/tagrendering.css index 71713b2d7..0b04ee636 100644 --- a/css/tagrendering.css +++ b/css/tagrendering.css @@ -1,11 +1,10 @@ .featureinfobox-title { - background-color: deeppink; + font-size: xx-large; } .featureinfobox-icons img{ max-height: 1.5em; } .featureinfobox-icons { - background-color: red; } .featureinfobox-titlebar{ diff --git a/test.ts b/test.ts index 68aaa797e..a6385b141 100644 --- a/test.ts +++ b/test.ts @@ -6,29 +6,42 @@ import {TagRenderingConfigJson} from "./Customizations/JSON/TagRenderingConfigJs import TagRenderingConfig from "./Customizations/JSON/TagRenderingConfig"; import Locale from "./UI/i18n/Locale"; import EditableTagRendering from "./UI/Popup/EditableTagRendering"; +import TagRenderingQuestion from "./UI/Popup/TagRenderingQuestion"; const tagRendering: TagRenderingConfigJson = { question: {"en": "What is the name of?", nl: "Wat is de naam van?", fr: "C'est quoi le nom"}, mappings: [ { - if: "noname=yes", - then: "Has no name" + if: "valves=A", + then: "A" + }, + { + if: "valves=B", + then: "B" + }, + { + if: "valves=C", + then: "C" + }, { + if: "valves:special=A", + then: "SPecial" } ], - render: "The name is {name}", + render: "Valves: {valves}", + multiAnswer: true, freeform: { - key: "name", + key: "valves", type: "string", - addExtraTags: ["noname="] - }//*/ + addExtraTags: ["fixme=valves"] + } } const config = new TagRenderingConfig(tagRendering) const tags = new UIEventSource({id: "node/-1", "amenity": "bench", name: "pietervdvn"}) -// new TagRenderingQuestion(tags, config).AttachTo("maindiv") -new EditableTagRendering(tags, config).AttachTo('maindiv') + new TagRenderingQuestion(tags, config).AttachTo("maindiv") +// new EditableTagRendering(tags, config).AttachTo('maindiv') Locale.CreateLanguagePicker(["nl", "en", "fr"]).AttachTo("extradiv") /*/