From 9c147b6ec6cc76251200732db22f547a1c358ce1 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 01:51:13 +0200 Subject: [PATCH 1/2] Fix lineheight, fix #511 --- css/openinghourstable.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/css/openinghourstable.css b/css/openinghourstable.css index b477b4862..877355426 100644 --- a/css/openinghourstable.css +++ b/css/openinghourstable.css @@ -16,6 +16,8 @@ border-collapse: collapse; background-clip: padding-box; border-right: 1px solid #ccc; + /* Somthing sets linehight to 1.5, but the leaflet-popup-content sets it to 1.4, throwing of the calculations... */ + line-height: 1.5 !important; } .oh-timecell { From a2306c2c6f670e87defca53bbc20a20a68cfec51 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 29 Oct 2021 03:42:33 +0200 Subject: [PATCH 2/2] Add the possibility to define a postfix and prefix for opening hours --- Docs/CalculatedTags.md | 2 +- Docs/SpecialInputElements.md | 68 +++++- Docs/SpecialRenderings.md | 8 +- UI/Input/InputElementMap.ts | 2 +- UI/Input/ValidatedTextField.ts | 217 +++++++++++++------ UI/OpeningHours/OpeningHoursInput.ts | 38 +++- UI/OpeningHours/OpeningHoursVisualization.ts | 10 +- UI/SpecialVisualizations.ts | 11 +- UI/SubstitutedTranslation.ts | 4 +- assets/layers/bike_shop/bike_shop.json | 2 +- 10 files changed, 281 insertions(+), 81 deletions(-) diff --git a/Docs/CalculatedTags.md b/Docs/CalculatedTags.md index b9bfe2104..9e0ce6fa7 100644 --- a/Docs/CalculatedTags.md +++ b/Docs/CalculatedTags.md @@ -100,7 +100,7 @@ Adds the time that the data got loaded - pretty much the time of downloading fro -### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number +### _last_edit:contributor, _last_edit:contributor:uid, _last_edit:changeset, _last_edit:timestamp, _version_number, _backend diff --git a/Docs/SpecialInputElements.md b/Docs/SpecialInputElements.md index 3c05b4c87..5a70109de 100644 --- a/Docs/SpecialInputElements.md +++ b/Docs/SpecialInputElements.md @@ -24,7 +24,40 @@ A geographical length in meters (rounded at two points). Will give an extra mini ## wikidata -A wikidata identifier, e.g. Q42. Input helper arguments: [ key: the value of this tag will initialize search (default: name), options: { removePrefixes: string[], removePostfixes: string[] } these prefixes and postfixes will be removed from the initial search value] +A wikidata identifier, e.g. Q42. +### Helper arguments + + + +name | doc +------ | ----- +key | the value of this tag will initialize search (default: name) +options | A JSON-object of type `{ removePrefixes: string[], removePostfixes: string[] }`. + +subarg | doc +-------- | ----- +removePrefixes | remove these snippets of text from the start of the passed string to search +removePostfixes | remove these snippets of text from the end of the passed string to search + + +### Example usage + + The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name```"freeform": { + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "street", + "boulevard", + "path", + "square", + "plaza", + ] + } + ] + },``` ## int @@ -60,7 +93,38 @@ A phone number ## opening_hours -Has extra elements to easily input when a POI is opened +Has extra elements to easily input when a POI is opened. +### Helper arguments + + + +name | doc +------ | ----- +options | A JSON-object of type `{ prefix: string, postfix: string }`. + +subarg | doc +-------- | ----- +prefix | Piece of text that will always be added to the front of the generated opening hours. If the OSM-data does not start with this, it will fail to parse +postfix | Piece of text that will always be added to the end of the generated opening hours + + +### Example usage + + To add a conditional (based on time) access restriction: + +``` + "freeform": { + "key": "access:conditional", + "type": "opening_hours", + "helperArgs": [ + { + "prefix":"no @ (", + "postfix":")" + } + ] + },``` + +*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )` ## color diff --git a/Docs/SpecialRenderings.md b/Docs/SpecialRenderings.md index 7fff50797..77b5592e3 100644 --- a/Docs/SpecialRenderings.md +++ b/Docs/SpecialRenderings.md @@ -14,11 +14,11 @@ name | default | description ------ | --------- | ------------- -image key/prefix (multiple values allowed if comma-seperated) | image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... +image key/prefix (multiple values allowed if comma-seperated) | image,mapillary,image,wikidata,wikimedia_commons,image,image | The keys given to the images, e.g. if image is given, the first picture URL will be added as image, the second as image:0, the third as image:1, etc... #### Example usage - `{image_carousel(image)}` + `{image_carousel(image,mapillary,image,wikidata,wikimedia_commons,image,image)}` ### image_upload Creates a button where a user can upload an image to IMGUR @@ -73,10 +73,12 @@ fallback | undefined | The identifier to use, if tags[subjectKey] as spec name | default | description ------ | --------- | ------------- key | opening_hours | The tagkey from which the table is constructed. +prefix | | Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__ +postfix | | Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__ #### Example usage - `{opening_hours_table(opening_hours)}` + A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}` ### live Downloads a JSON from the given URL, e.g. '{live(example.org/data.json, shorthand:x.y.z, other:a.b.c, shorthand)}' will download the given file, will create an object {shorthand: json[x][y][z], other: json[a][b][c] out of it and will return 'other' or 'json[a][b][c]. This is made to use in combination with tags, e.g. {live({url}, {url:format}, needed_value)} diff --git a/UI/Input/InputElementMap.ts b/UI/Input/InputElementMap.ts index 548e50363..a2a50f9d3 100644 --- a/UI/Input/InputElementMap.ts +++ b/UI/Input/InputElementMap.ts @@ -25,8 +25,8 @@ export default class InputElementMap extends InputElement { const self = this; this._value = inputElement.GetValue().map( (t => { - const currentX = self.GetValue()?.data; const newX = toX(t); + const currentX = self.GetValue()?.data; if (isSame(currentX, newX)) { return currentX; } diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts index ad5554346..71db4a5b3 100644 --- a/UI/Input/ValidatedTextField.ts +++ b/UI/Input/ValidatedTextField.ts @@ -19,6 +19,9 @@ import {FixedInputElement} from "./FixedInputElement"; import WikidataSearchBox from "../Wikipedia/WikidataSearchBox"; import Wikidata from "../../Logic/Web/Wikidata"; import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"; +import Table from "../Base/Table"; +import Combine from "../Base/Combine"; +import Title from "../Base/Title"; interface TextFieldDef { name: string, @@ -28,12 +31,159 @@ interface TextFieldDef { inputHelper?: (value: UIEventSource, options?: { location: [number, number], mapBackgroundLayer?: UIEventSource, - args: (string | number | boolean)[] + args: (string | number | boolean | any)[] feature?: any }) => InputElement, inputmode?: string } +class WikidataTextField implements TextFieldDef { + name = "wikidata" + explanation = + new Combine([ + "A wikidata identifier, e.g. Q42.", + new Title("Helper arguments"), + new Table(["name", "doc"], + [ + ["key", "the value of this tag will initialize search (default: name)"], + ["options", new Combine(["A JSON-object of type `{ removePrefixes: string[], removePostfixes: string[] }`.", + new Table( + ["subarg", "doc"], + [["removePrefixes", "remove these snippets of text from the start of the passed string to search"], + ["removePostfixes", "remove these snippets of text from the end of the passed string to search"], + ] + )]) + ]]), + new Title("Example usage"), + "The following is the 'freeform'-part of a layer config which will trigger a search for the wikidata item corresponding with the name of the selected feature. It will also remove '-street', '-square', ... if found at the end of the name```" + `"freeform": { + "key": "name:etymology:wikidata", + "type": "wikidata", + "helperArgs": [ + "name", + { + "removePostfixes": [ + "street", + "boulevard", + "path", + "square", + "plaza", + ] + } + ] + },` + "```" + ]).AsMarkdown() + + + public isValid(str) { + + if (str === undefined) { + return false; + } + if (str.length <= 2) { + return false; + } + return !str.split(";").some(str => Wikidata.ExtractKey(str) === undefined) + } + + public reformat(str) { + if (str === undefined) { + return undefined; + } + let out = str.split(";").map(str => Wikidata.ExtractKey(str)).join("; ") + if (str.endsWith(";")) { + out = out + ";" + } + return out; + } + + public inputHelper(currentValue, inputHelperOptions) { + const args = inputHelperOptions.args ?? [] + const searchKey = args[0] ?? "name" + + let searchFor = inputHelperOptions.feature?.properties[searchKey]?.toLowerCase() + + const options = args[1] + if (searchFor !== undefined && options !== undefined) { + const prefixes = options["removePrefixes"] + const postfixes = options["removePostfixes"] + for (const postfix of postfixes ?? []) { + if (searchFor.endsWith(postfix)) { + searchFor = searchFor.substring(0, searchFor.length - postfix.length) + break; + } + } + + for (const prefix of prefixes ?? []) { + if (searchFor.startsWith(prefix)) { + searchFor = searchFor.substring(prefix.length) + break; + } + } + + } + + return new WikidataSearchBox({ + value: currentValue, + searchText: new UIEventSource(searchFor) + }) + } +} + +class OpeningHoursTextField implements TextFieldDef { + name = "opening_hours" + explanation = + new Combine([ + "Has extra elements to easily input when a POI is opened.", + new Title("Helper arguments"), + new Table(["name", "doc"], + [ + ["options", new Combine([ + "A JSON-object of type `{ prefix: string, postfix: string }`. ", + new Table(["subarg", "doc"], + [ + ["prefix", "Piece of text that will always be added to the front of the generated opening hours. If the OSM-data does not start with this, it will fail to parse"], + ["postfix", "Piece of text that will always be added to the end of the generated opening hours"], + ]) + + ]) + ] + ]), + new Title("Example usage"), + "To add a conditional (based on time) access restriction:\n\n```" + ` + "freeform": { + "key": "access:conditional", + "type": "opening_hours", + "helperArgs": [ + { + "prefix":"no @ (", + "postfix":")" + } + ] + },` + "```\n\n*Don't forget to pass these in the rendering as well*: `{opening_hours_table(opening_hours,yes @ &LPARENS, &RPARENS )`"]).AsMarkdown() + + + isValid() { + return true + } + + reformat(str) { + return str + } + + inputHelper(value: UIEventSource, inputHelperOptions: { + location: [number, number], + mapBackgroundLayer?: UIEventSource, + args: (string | number | boolean | any)[] + feature?: any + }) { + const args = (inputHelperOptions.args ?? [])[0] + const prefix = args?.prefix ?? "" + const postfix = args?.postfix ?? "" + + return new OpeningHoursInput(value, prefix, postfix) + } +} + export default class ValidatedTextField { public static tpList: TextFieldDef[] = [ @@ -146,60 +296,7 @@ export default class ValidatedTextField { }, "decimal" ), - ValidatedTextField.tp( - "wikidata", - "A wikidata identifier, e.g. Q42. Input helper arguments: [ key: the value of this tag will initialize search (default: name), options: { removePrefixes: string[], removePostfixes: string[] } these prefixes and postfixes will be removed from the initial search value]", - (str) => { - if (str === undefined) { - return false; - } - if(str.length <= 2){ - return false; - } - return !str.split(";").some(str => Wikidata.ExtractKey(str) === undefined) - }, - (str) => { - if (str === undefined) { - return undefined; - } - let out = str.split(";").map(str => Wikidata.ExtractKey(str)).join("; ") - if(str.endsWith(";")){ - out = out + ";" - } - return out; - }, - (currentValue, inputHelperOptions) => { - const args = inputHelperOptions.args ?? [] - const searchKey = args[0] ?? "name" - - let searchFor = inputHelperOptions.feature?.properties[searchKey]?.toLowerCase() - - const options = args[1] - if (searchFor !== undefined && options !== undefined) { - const prefixes = options["removePrefixes"] - const postfixes = options["removePostfixes"] - for (const postfix of postfixes ?? []) { - if (searchFor.endsWith(postfix)) { - searchFor = searchFor.substring(0, searchFor.length - postfix.length) - break; - } - } - - for (const prefix of prefixes ?? []) { - if (searchFor.startsWith(prefix)) { - searchFor = searchFor.substring(prefix.length) - break; - } - } - - } - - return new WikidataSearchBox({ - value: currentValue, - searchText: new UIEventSource(searchFor) - }) - } - ), + new WikidataTextField(), ValidatedTextField.tp( "int", @@ -299,15 +396,7 @@ export default class ValidatedTextField { undefined, "tel" ), - ValidatedTextField.tp( - "opening_hours", - "Has extra elements to easily input when a POI is opened", - () => true, - str => str, - (value) => { - return new OpeningHoursInput(value); - } - ), + new OpeningHoursTextField(), ValidatedTextField.tp( "color", "Shows a color picker", diff --git a/UI/OpeningHours/OpeningHoursInput.ts b/UI/OpeningHours/OpeningHoursInput.ts index d8d5ce16b..88b061a3d 100644 --- a/UI/OpeningHours/OpeningHoursInput.ts +++ b/UI/OpeningHours/OpeningHoursInput.ts @@ -23,11 +23,39 @@ export default class OpeningHoursInput extends InputElement { private readonly _value: UIEventSource; private readonly _element: BaseUIElement; - constructor(value: UIEventSource = new UIEventSource("")) { + constructor(value: UIEventSource = new UIEventSource(""), prefix = "", postfix = "") { super(); this._value = value; + let valueWithoutPrefix = value + if (prefix !== "" && postfix !== "") { + + valueWithoutPrefix = value.map(str => { + if (str === undefined) { + return undefined; + } + if(str === ""){ + return "" + } + if (str.startsWith(prefix) && str.endsWith(postfix)) { + return str.substring(prefix.length, str.length - postfix.length) + } + return str + }, [], noPrefix => { + if (noPrefix === undefined) { + return undefined; + } + if(noPrefix === ""){ + return "" + } + if (noPrefix.startsWith(prefix) && noPrefix.endsWith(postfix)) { + return noPrefix + } - const leftoverRules = value.map(str => { + return prefix + noPrefix + postfix + }) + } + + const leftoverRules = valueWithoutPrefix.map(str => { if (str === undefined) { return [] } @@ -45,9 +73,9 @@ export default class OpeningHoursInput extends InputElement { return leftOvers; }) // Note: MUST be bound AFTER the leftover rules! - const rulesFromOhPicker = value.map(OH.Parse); + const rulesFromOhPicker = valueWithoutPrefix.map(OH.Parse); - const ph = value.map(str => { + const ph = valueWithoutPrefix.map(str => { if (str === undefined) { return "" } @@ -68,7 +96,7 @@ export default class OpeningHoursInput extends InputElement { ...leftoverRules.data, ph.data ] - value.setData(Utils.NoEmpty(rules).join(";")); + valueWithoutPrefix.setData(Utils.NoEmpty(rules).join(";")); } rulesFromOhPicker.addCallback(update); diff --git a/UI/OpeningHours/OpeningHoursVisualization.ts b/UI/OpeningHours/OpeningHoursVisualization.ts index 785d046a9..fb8ae05d6 100644 --- a/UI/OpeningHours/OpeningHoursVisualization.ts +++ b/UI/OpeningHours/OpeningHoursVisualization.ts @@ -23,10 +23,16 @@ export default class OpeningHoursVisualization extends Toggle { Translations.t.general.weekdays.abbreviations.sunday, ] - constructor(tags: UIEventSource, key: string) { + constructor(tags: UIEventSource, key: string, prefix = "", postfix = "") { const tagsDirect = tags.data; const ohTable = new VariableUiElement(tags - .map(tags => tags[key]) // This mapping will absorb all other changes to tags in order to prevent regeneration + .map(tags => { + const value : string = tags[key]; + if(value.startsWith(prefix) && value.endsWith(postfix)){ + return value.substring(prefix.length, value.length - postfix.length) + } + return value; + }) // This mapping will absorb all other changes to tags in order to prevent regeneration .map(ohtext => { try { // noinspection JSPotentiallyInvalidConstructorUsage diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts index 680b3e571..fa3362107 100644 --- a/UI/SpecialVisualizations.ts +++ b/UI/SpecialVisualizations.ts @@ -253,9 +253,18 @@ export default class SpecialVisualizations { name: "key", defaultValue: "opening_hours", doc: "The tagkey from which the table is constructed." + },{ + name: "prefix", + defaultValue: "", + doc:"Remove this string from the start of the value before parsing. __Note: use `&LPARENs` to indicate `(` if needed__" + },{ + name: "postfix", + defaultValue: "", + doc:"Remove this string from the end of the value before parsing. __Note: use `&RPARENs` to indicate `)` if needed__" }], + example: "A normal opening hours table can be invoked with `{opening_hours_table()}`. A table for e.g. conditional access with opening hours can be `{opening_hours_table(access:conditional, no @ &LPARENS, &RPARENS)}`", constr: (state: State, tagSource: UIEventSource, args) => { - return new OpeningHoursVisualization(tagSource, args[0]) + return new OpeningHoursVisualization(tagSource, args[0], args[1], args[2]) } }, { diff --git a/UI/SubstitutedTranslation.ts b/UI/SubstitutedTranslation.ts index 361540e65..ff2d3a447 100644 --- a/UI/SubstitutedTranslation.ts +++ b/UI/SubstitutedTranslation.ts @@ -85,7 +85,9 @@ export class SubstitutedTranslation extends VariableUiElement { const partAfter = SubstitutedTranslation.ExtractSpecialComponents(matched[4], extraMappings); const args = knownSpecial.args.map(arg => arg.defaultValue ?? ""); if (argument.length > 0) { - const realArgs = argument.split(",").map(str => str.trim()); + const realArgs = argument.split(",").map(str => str.trim() + .replace(/&LPARENS/g, '(') + .replace(/&RPARENS/g, ')')); for (let i = 0; i < realArgs.length; i++) { if (args.length <= i) { args.push(realArgs[i]); diff --git a/assets/layers/bike_shop/bike_shop.json b/assets/layers/bike_shop/bike_shop.json index 6cd8890c3..1be9adee9 100644 --- a/assets/layers/bike_shop/bike_shop.json +++ b/assets/layers/bike_shop/bike_shop.json @@ -298,7 +298,7 @@ "id": "bike_shop-email" }, { - "render": "{opening_hours_table(opening_hours)}", + "render": "{opening_hours_table()}", "question": "When is this shop opened?", "freeform": { "key": "opening_hours",