Better tag rewriting, add icons, add bicycle rental theme
This commit is contained in:
parent
1dcb3897e4
commit
9594868e83
23 changed files with 389 additions and 117 deletions
|
@ -49,8 +49,26 @@ export default class SharedTagRenderings {
|
|||
return
|
||||
}
|
||||
value.id = value.id ?? key;
|
||||
if(value["builtin"] !== undefined){
|
||||
if(value["override"] == undefined){
|
||||
throw "HUH? Why whould you want to reuse a builtin if one doesn't override? In questions.json/"+key
|
||||
}
|
||||
if(typeof value["builtin"] !== "string"){
|
||||
return;
|
||||
}
|
||||
// This is a really funny situation: we extend another tagRendering!
|
||||
const parent = Utils.Clone(dict.get(value["builtin"]))
|
||||
delete parent.id
|
||||
Utils.Merge(value["override"], parent)
|
||||
delete value["builtin"]
|
||||
delete value["override"]
|
||||
for (const pkey in parent) {
|
||||
value[pkey] = parent[pkey]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import SharedTagRenderings from "../Customizations/SharedTagRenderings";
|
|||
import * as known_layers from "../assets/generated/known_layers.json"
|
||||
import {LayoutConfigJson} from "../Models/ThemeConfig/Json/LayoutConfigJson";
|
||||
import {PrepareTheme} from "../Models/ThemeConfig/Conversion/PrepareTheme";
|
||||
import {Layer} from "leaflet";
|
||||
|
||||
export default class DetermineLayout {
|
||||
|
||||
|
@ -134,9 +135,9 @@ export default class DetermineLayout {
|
|||
|
||||
private static prepCustomTheme(json: any): LayoutConfigJson {
|
||||
const knownLayersDict = new Map<string, LayerConfigJson>()
|
||||
for (const key in known_layers["default"]) {
|
||||
const layer = known_layers["default"][key]
|
||||
knownLayersDict.set(layer.id, layer)
|
||||
for (const key in known_layers.layers) {
|
||||
const layer = known_layers.layers[key]
|
||||
knownLayersDict.set(layer.id,<LayerConfigJson> layer)
|
||||
}
|
||||
const converState = {
|
||||
tagRenderings: SharedTagRenderings.SharedTagRenderingJson,
|
||||
|
|
|
@ -2,6 +2,8 @@ import {Conversion, DesugaringContext, Fuse, OnEveryConcat, SetDefault} from "./
|
|||
import {LayerConfigJson} from "../Json/LayerConfigJson";
|
||||
import {TagRenderingConfigJson} from "../Json/TagRenderingConfigJson";
|
||||
import {Utils} from "../../../Utils";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {Translation} from "../../../UI/i18n/Translation";
|
||||
|
||||
class ExpandTagRendering extends Conversion<string | TagRenderingConfigJson | { builtin: string | string[], override: any }, TagRenderingConfigJson[]> {
|
||||
constructor() {
|
||||
|
@ -156,13 +158,36 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
}
|
||||
let config = <{
|
||||
rewrite:
|
||||
{ sourceString: string; into: string[] }[];
|
||||
{ sourceString: string[]; into: (string | any)[][] };
|
||||
renderings: (string | { builtin: string; override: any } | TagRenderingConfigJson)[]
|
||||
}>json;
|
||||
|
||||
{
|
||||
const errors = []
|
||||
|
||||
const expectedLength = config.rewrite.sourceString.length
|
||||
for (let i = 0; i < config.rewrite.into.length; i++){
|
||||
const targets = config.rewrite.into[i];
|
||||
if(targets.length !== expectedLength){
|
||||
errors.push(context+".rewrite.into["+i+"]: expected "+expectedLength+" values, but got "+targets.length)
|
||||
}
|
||||
if(typeof targets[0] !== "string"){
|
||||
errors.push(context+".rewrite.into["+i+"]: expected a string as first rewrite value values, but got "+targets[0])
|
||||
|
||||
const subRenderingsRes = ExpandGroupRewrite.expandSubTagRenderings.convertAll(state, config.renderings, context);
|
||||
const subRenderings: TagRenderingConfigJson[] = [].concat(subRenderingsRes.result);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
return {
|
||||
errors,
|
||||
warnings: [],
|
||||
result: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const subRenderingsRes = <{ result: TagRenderingConfigJson[][], errors, warnings }> ExpandGroupRewrite.expandSubTagRenderings.convertAll(state, config.renderings, context);
|
||||
const subRenderings: TagRenderingConfigJson[] = [].concat(...subRenderingsRes.result);
|
||||
const errors = subRenderingsRes.errors;
|
||||
const warnings = subRenderingsRes.warnings;
|
||||
|
||||
|
@ -170,22 +195,31 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
const rewrittenPerGroup = new Map<string, TagRenderingConfigJson[]>()
|
||||
|
||||
// The actual rewriting
|
||||
for (const rewrite of config.rewrite) {
|
||||
const source = rewrite.sourceString;
|
||||
for (const target of rewrite.into) {
|
||||
const groupName = target;
|
||||
const trs: TagRenderingConfigJson[] = []
|
||||
const sourceStrings = config.rewrite.sourceString;
|
||||
for (const targets of config.rewrite.into) {
|
||||
const groupName = targets[0];
|
||||
if(typeof groupName !== "string"){
|
||||
throw "The first string of 'targets' should always be a string"
|
||||
}
|
||||
const trs: TagRenderingConfigJson[] = []
|
||||
|
||||
for (const tr of subRenderings) {
|
||||
trs.push(this.prepConfig(source, target, tr))
|
||||
for (const tr of subRenderings) {
|
||||
let rewritten = tr;
|
||||
for (let i = 0; i < sourceStrings.length; i++) {
|
||||
const source = sourceStrings[i]
|
||||
const target = targets[i] // This is a string OR a translation
|
||||
console.log("Replacing every "+source+" with "+JSON.stringify(target))
|
||||
rewritten = this.prepConfig(source, target, rewritten)
|
||||
}
|
||||
if (rewrittenPerGroup.has(groupName)) {
|
||||
rewrittenPerGroup.get(groupName).push(...trs)
|
||||
rewritten.group = rewritten.group ?? groupName
|
||||
trs.push(rewritten)
|
||||
}
|
||||
|
||||
} else {
|
||||
rewrittenPerGroup.set(groupName, trs)
|
||||
if (rewrittenPerGroup.has(groupName)) {
|
||||
rewrittenPerGroup.get(groupName).push(...trs)
|
||||
} else {
|
||||
rewrittenPerGroup.set(groupName, trs)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,7 +235,7 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
rewrittenPerGroup.forEach((group, _) => {
|
||||
group.forEach(tr => {
|
||||
if (tr.id === undefined || tr.id === "") {
|
||||
errors.push("A tagrendering has an empty ID after expanding the tag")
|
||||
errors.push("A tagrendering has an empty ID after expanding the tag; the tagrendering is: "+JSON.stringify(tr))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -212,16 +246,26 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
};
|
||||
}
|
||||
|
||||
/* Used for left|right group creation and replacement */
|
||||
private prepConfig(keyToRewrite: string, target: string, tr: TagRenderingConfigJson) {
|
||||
/* Used for left|right group creation and replacement.
|
||||
* Every 'keyToRewrite' will be replaced with 'target' recursively. This substitution will happen in place in the object 'tr' */
|
||||
private prepConfig(keyToRewrite: string, target: string | any, tr: TagRenderingConfigJson): TagRenderingConfigJson {
|
||||
|
||||
const isTranslation = typeof target !== "string"
|
||||
|
||||
function replaceRecursive(transl: string | any) {
|
||||
if (typeof transl === "string") {
|
||||
// This is a simple string - we do a simple replace
|
||||
return transl.replace(keyToRewrite, target)
|
||||
}
|
||||
if (transl.map !== undefined) {
|
||||
// This is a list of items
|
||||
return transl.map(o => replaceRecursive(o))
|
||||
}
|
||||
|
||||
if (Translations.isProbablyATranslation(transl) && isTranslation) {
|
||||
return Translations.T(transl).Fuse(new Translation(target), keyToRewrite).translations
|
||||
}
|
||||
|
||||
transl = {...transl}
|
||||
for (const key in transl) {
|
||||
transl[key] = replaceRecursive(transl[key])
|
||||
|
@ -229,12 +273,7 @@ class ExpandGroupRewrite extends Conversion<{
|
|||
return transl
|
||||
}
|
||||
|
||||
const orig = tr;
|
||||
tr = replaceRecursive(tr)
|
||||
|
||||
tr.id = target + "-" + orig.id
|
||||
tr.group = target
|
||||
return tr
|
||||
return replaceRecursive(tr)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -225,9 +225,9 @@ export interface LayerConfigJson {
|
|||
*/
|
||||
tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson | {
|
||||
rewrite: {
|
||||
sourceString: string,
|
||||
into: string[]
|
||||
}[],
|
||||
sourceString: string[],
|
||||
into: (string | any)[][]
|
||||
},
|
||||
renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[]
|
||||
}) [],
|
||||
|
||||
|
|
|
@ -106,6 +106,10 @@ export interface TagRenderingConfigJson {
|
|||
* If not known yet, the user will be presented with `then` as an option
|
||||
*/
|
||||
then: string | any,
|
||||
/**
|
||||
* An icon supporting this mapping; typically shown pretty small
|
||||
*/
|
||||
icon?: string
|
||||
/**
|
||||
* In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation).
|
||||
*
|
||||
|
|
|
@ -41,7 +41,8 @@ export default class TagRenderingConfig {
|
|||
readonly mappings?: {
|
||||
readonly if: TagsFilter,
|
||||
readonly ifnot?: TagsFilter,
|
||||
readonly then: Translation
|
||||
readonly then: Translation,
|
||||
readonly icon: string,
|
||||
readonly hideInAnswer: boolean | TagsFilter
|
||||
readonly addExtraTags: Tag[]
|
||||
}[]
|
||||
|
@ -163,11 +164,16 @@ export default class TagRenderingConfig {
|
|||
} else if (mapping.hideInAnswer !== undefined) {
|
||||
hideInAnswer = TagUtils.Tag(mapping.hideInAnswer, `${context}.mapping[${i}].hideInAnswer`);
|
||||
}
|
||||
let icon = undefined;
|
||||
if(mapping.icon !== ""){
|
||||
icon = mapping.icon
|
||||
}
|
||||
const mp = {
|
||||
if: TagUtils.Tag(mapping.if, `${ctx}.if`),
|
||||
ifnot: (mapping.ifnot !== undefined ? TagUtils.Tag(mapping.ifnot, `${ctx}.ifnot`) : undefined),
|
||||
then: Translations.T(mapping.then, `${ctx}.then`),
|
||||
hideInAnswer: hideInAnswer,
|
||||
hideInAnswer,
|
||||
icon,
|
||||
addExtraTags: (mapping.addExtraTags ?? []).map((str, j) => TagUtils.SimpleTag(str, `${ctx}.addExtraTags[${j}]`))
|
||||
};
|
||||
if (this.question) {
|
||||
|
@ -329,18 +335,18 @@ export default class TagRenderingConfig {
|
|||
* @param tags
|
||||
* @constructor
|
||||
*/
|
||||
public GetRenderValues(tags: any): Translation[] {
|
||||
public GetRenderValues(tags: any): {then: Translation, icon?: string}[] {
|
||||
if (!this.multiAnswer) {
|
||||
return [this.GetRenderValue(tags)]
|
||||
return [this.GetRenderValueWithImage(tags)]
|
||||
}
|
||||
|
||||
// A flag to check that the freeform key isn't matched multiple times
|
||||
// If it is undefined, it is "used" already, or at least we don't have to check for it anymore
|
||||
let freeformKeyUsed = this.freeform?.key === undefined;
|
||||
// We run over all the mappings first, to check if the mapping matches
|
||||
const applicableMappings: Translation[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
|
||||
const applicableMappings: {then: Translation, img?: string}[] = Utils.NoNull((this.mappings ?? [])?.map(mapping => {
|
||||
if (mapping.if === undefined) {
|
||||
return mapping.then;
|
||||
return mapping;
|
||||
}
|
||||
if (TagUtils.MatchesMultiAnswer(mapping.if, tags)) {
|
||||
if (!freeformKeyUsed) {
|
||||
|
@ -349,7 +355,7 @@ export default class TagRenderingConfig {
|
|||
freeformKeyUsed = true;
|
||||
}
|
||||
}
|
||||
return mapping.then;
|
||||
return mapping;
|
||||
}
|
||||
return undefined;
|
||||
}))
|
||||
|
@ -357,43 +363,42 @@ export default class TagRenderingConfig {
|
|||
|
||||
if (!freeformKeyUsed
|
||||
&& tags[this.freeform.key] !== undefined) {
|
||||
applicableMappings.push(this.render)
|
||||
applicableMappings.push({then: this.render})
|
||||
}
|
||||
return applicableMappings
|
||||
}
|
||||
|
||||
public GetRenderValue(tags: any, defltValue: any = undefined): Translation {
|
||||
return this.GetRenderValueWithImage(tags, defltValue).then
|
||||
}
|
||||
/**
|
||||
* Gets the correct rendering value (or undefined if not known)
|
||||
* Not compatible with multiAnswer - use GetRenderValueS instead in that case
|
||||
* @constructor
|
||||
*/
|
||||
public GetRenderValue(tags: any, defltValue: any = undefined): Translation {
|
||||
public GetRenderValueWithImage(tags: any, defltValue: any = undefined): { then: Translation, icon?: string } {
|
||||
if (this.mappings !== undefined && !this.multiAnswer) {
|
||||
for (const mapping of this.mappings) {
|
||||
if (mapping.if === undefined) {
|
||||
return mapping.then;
|
||||
return mapping;
|
||||
}
|
||||
if (mapping.if.matchesProperties(tags)) {
|
||||
if (this.id === "uk_addresses_placename") {
|
||||
console.log("Matched", mapping.if, "with ", tags["addr:place"])
|
||||
}
|
||||
return mapping.then;
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.id === "questions") {
|
||||
return this.render
|
||||
if (this.id === "questions" ||
|
||||
this.freeform?.key === undefined ||
|
||||
tags[this.freeform.key] !== undefined
|
||||
) {
|
||||
return {then: this.render}
|
||||
}
|
||||
|
||||
if (this.freeform?.key === undefined) {
|
||||
return this.render;
|
||||
}
|
||||
|
||||
if (tags[this.freeform.key] !== undefined) {
|
||||
return this.render;
|
||||
}
|
||||
return defltValue;
|
||||
return {then: defltValue};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,6 @@ export default class TableOfContents extends Combine {
|
|||
let els: { level: number, content: BaseUIElement }[] = []
|
||||
for (const title of titles) {
|
||||
let content: BaseUIElement
|
||||
console.log("Constructing content for ", title)
|
||||
if (title.title instanceof Translation) {
|
||||
content = title.title.Clone()
|
||||
} else if (title.title instanceof FixedUiElement) {
|
||||
|
|
|
@ -5,6 +5,8 @@ import {VariableUiElement} from "../Base/VariableUIElement";
|
|||
import List from "../Base/List";
|
||||
import {SubstitutedTranslation} from "../SubstitutedTranslation";
|
||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
|
||||
import Combine from "../Base/Combine";
|
||||
import Img from "../Base/Img";
|
||||
|
||||
/***
|
||||
* Displays the correct value for a known tagrendering
|
||||
|
@ -38,11 +40,17 @@ export default class TagRenderingAnswer extends VariableUiElement {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const valuesToRender: BaseUIElement[] = trs.map(tr => new SubstitutedTranslation(tr, tagsSource, state, options?.specialViz))
|
||||
const valuesToRender: BaseUIElement[] = trs.map(tr => {
|
||||
const text = new SubstitutedTranslation(tr.then, tagsSource, state, options?.specialViz);
|
||||
if(tr.icon === undefined){
|
||||
return text
|
||||
}
|
||||
return new Combine([new Img(tr.icon).SetClass("w-6 max-h-6 pr-2"), text]).SetClass("flex")
|
||||
})
|
||||
if (valuesToRender.length === 1) {
|
||||
return valuesToRender[0];
|
||||
} else if (valuesToRender.length > 1) {
|
||||
return new List(valuesToRender)
|
||||
return new Combine(valuesToRender).SetClass("flex flex-col")
|
||||
}
|
||||
return undefined;
|
||||
}).map((element: BaseUIElement) => element?.SetClass(contentClasses)?.SetStyle(contentStyle)))
|
||||
|
|
|
@ -26,6 +26,10 @@ import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig";
|
|||
import {Unit} from "../../Models/Unit";
|
||||
import VariableInputElement from "../Input/VariableInputElement";
|
||||
import Toggle from "../Input/Toggle";
|
||||
import Img from "../Base/Img";
|
||||
import {flattenEach, tag} from "@turf/turf";
|
||||
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline";
|
||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState";
|
||||
|
||||
/**
|
||||
* Shows the question element.
|
||||
|
@ -343,7 +347,8 @@ export default class TagRenderingQuestion extends Combine {
|
|||
mapping: {
|
||||
if: TagsFilter,
|
||||
then: Translation,
|
||||
addExtraTags: Tag[]
|
||||
addExtraTags: Tag[],
|
||||
img?: string
|
||||
}, ifNot?: TagsFilter[]): InputElement<TagsFilter> {
|
||||
|
||||
let tagging: TagsFilter = mapping.if;
|
||||
|
@ -354,11 +359,23 @@ export default class TagRenderingQuestion extends Combine {
|
|||
tagging = new And([tagging, ...mapping.addExtraTags])
|
||||
}
|
||||
|
||||
|
||||
return new FixedInputElement(
|
||||
new SubstitutedTranslation(mapping.then, tagsSource, state),
|
||||
TagRenderingQuestion.GenerateMappingContent(mapping, tagsSource, state) ,
|
||||
tagging,
|
||||
(t0, t1) => t1.isEquivalent(t0));
|
||||
}
|
||||
|
||||
private static GenerateMappingContent( mapping: {
|
||||
then: Translation,
|
||||
icon?: string
|
||||
}, tagsSource: UIEventSource<any>, state: FeaturePipelineState): BaseUIElement{
|
||||
const text = new SubstitutedTranslation(mapping.then, tagsSource, state)
|
||||
if (mapping.icon === undefined) {
|
||||
return text;
|
||||
}
|
||||
return new Combine([new Img(mapping.icon).SetClass("w-6 max-h-6 pr-2"), text]).SetClass("flex")
|
||||
}
|
||||
|
||||
private static GenerateFreeform(state, configuration: TagRenderingConfig, applicableUnit: Unit, tags: UIEventSource<any>): InputElement<TagsFilter> {
|
||||
const freeform = configuration.freeform;
|
||||
|
|
|
@ -142,6 +142,33 @@ export class Translation extends BaseUIElement {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Given a translation such as `{en: "How much of bicycle_types are rented here}` (which is this translation)
|
||||
* and a translation object `{ en: "electrical bikes" }`, plus the translation specification `bicycle_types`, will return
|
||||
* a new translation:
|
||||
* `{en: "How much electrical bikes are rented here?"}`
|
||||
*
|
||||
* @param translationObject
|
||||
* @param stringToReplace
|
||||
* @constructor
|
||||
*/
|
||||
public Fuse(translationObject: Translation, stringToReplace: string): Translation{
|
||||
const translations = this.translations
|
||||
const newTranslations = {}
|
||||
for (const lang in translations) {
|
||||
const target = translationObject.textFor(lang)
|
||||
if(target === undefined){
|
||||
continue
|
||||
}
|
||||
if(typeof target !== "string"){
|
||||
throw "Invalid object in Translation.fuse: translationObject['"+lang+"'] is not a string, it is: "+JSON.stringify(target)
|
||||
}
|
||||
newTranslations[lang] = this.translations[lang].replaceAll(stringToReplace, target)
|
||||
}
|
||||
return new Translation(newTranslations)
|
||||
}
|
||||
|
||||
public replace(a: string, b: string) {
|
||||
if (a.startsWith("{") && a.endsWith("}")) {
|
||||
a = a.substr(1, a.length - 2);
|
||||
|
|
|
@ -2,11 +2,11 @@ import {FixedUiElement} from "../Base/FixedUiElement";
|
|||
import AllTranslationAssets from "../../AllTranslationAssets";
|
||||
import {Translation} from "./Translation";
|
||||
import BaseUIElement from "../BaseUIElement";
|
||||
|
||||
import * as known_languages from "../../assets/generated/used_languages.json"
|
||||
export default class Translations {
|
||||
|
||||
static t = AllTranslationAssets.t;
|
||||
|
||||
private static knownLanguages = new Set(known_languages.languages)
|
||||
constructor() {
|
||||
throw "Translations is static. If you want to intitialize a new translation, use the singular form"
|
||||
}
|
||||
|
@ -89,4 +89,11 @@ export default class Translations {
|
|||
|
||||
}
|
||||
|
||||
static isProbablyATranslation(transl: any) {
|
||||
if(typeof transl !== "object"){
|
||||
return false;
|
||||
}
|
||||
// is a weird key found?
|
||||
return !Object.keys(transl).some(key => !this.knownLanguages.has(key))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2975,32 +2975,14 @@
|
|||
},
|
||||
{
|
||||
"id": "payment-options",
|
||||
"builtin": "payment-options",
|
||||
"builtin": "payment-options-advanced",
|
||||
"override": {
|
||||
"condition": {
|
||||
"or": [
|
||||
"fee=yes",
|
||||
"charge~*"
|
||||
]
|
||||
},
|
||||
"mappings+": [
|
||||
{
|
||||
"if": "payment:app=yes",
|
||||
"ifnot": "payment:app=no",
|
||||
"then": {
|
||||
"en": "Payment is done using a dedicated app",
|
||||
"nl": "Betalen via een app van het netwerk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "payment:membership_card=yes",
|
||||
"ifnot": "payment:membership_card=no",
|
||||
"then": {
|
||||
"en": "Payment is done using a membership card",
|
||||
"nl": "Betalen via een lidkaart van het netwerk"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -260,32 +260,14 @@
|
|||
},
|
||||
{
|
||||
"id": "payment-options",
|
||||
"builtin": "payment-options",
|
||||
"builtin": "payment-options-advanced",
|
||||
"override": {
|
||||
"condition": {
|
||||
"or": [
|
||||
"fee=yes",
|
||||
"charge~*"
|
||||
]
|
||||
},
|
||||
"mappings+": [
|
||||
{
|
||||
"if": "payment:app=yes",
|
||||
"ifnot": "payment:app=no",
|
||||
"then": {
|
||||
"en": "Payment is done using a dedicated app",
|
||||
"nl": "Betalen via een app van het netwerk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "payment:membership_card=yes",
|
||||
"ifnot": "payment:membership_card=no",
|
||||
"then": {
|
||||
"en": "Payment is done using a membership card",
|
||||
"nl": "Betalen via een lidkaart van het netwerk"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"de": "Die Ebene zeigt Picknicktische an"
|
||||
},
|
||||
"tagRenderings": [
|
||||
"images",
|
||||
{
|
||||
"question": {
|
||||
"en": "What material is this picnic table made of?",
|
||||
|
|
7
assets/svg/cash.svg
Normal file
7
assets/svg/cash.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.3 KiB |
|
@ -141,6 +141,16 @@
|
|||
"https://commons.wikimedia.org/wiki/File:Camera_font_awesome.svg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "cash.svg",
|
||||
"license": "CC-BY 3.0",
|
||||
"authors": [
|
||||
"Online Web Fonts"
|
||||
],
|
||||
"sources": [
|
||||
"https://www.onlinewebfonts.com/icon/464494"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "checkbox-empty.svg",
|
||||
"license": "CC0",
|
||||
|
@ -1109,6 +1119,16 @@
|
|||
"authors": [],
|
||||
"sources": []
|
||||
},
|
||||
{
|
||||
"path": "smartphone.svg",
|
||||
"license": "CC-BY 3.0",
|
||||
"authors": [
|
||||
"To Uyen"
|
||||
],
|
||||
"sources": [
|
||||
"https://commons.wikimedia.org/wiki/File:Smartphone_icon_-_Noun_Project_283536.svg"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "speech_bubble.svg",
|
||||
"license": "CC-BY 4.0",
|
||||
|
|
4
assets/svg/smartphone.svg
Normal file
4
assets/svg/smartphone.svg
Normal file
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<path d="m33.975 95h32.05c5.565 0 10.08-4.279 10.08-9.569v-70.855c0-5.285-4.513-9.576-10.07-9.576h-32.05c-5.564 0-10.08 4.291-10.08 9.576v70.854c0 5.288 4.513 9.57 10.08 9.57m16.03-3.257c-2.493 0-4.506-2.02-4.506-4.498 0-2.489 2.01-4.502 4.506-4.502 2.487 0 4.494 2.01 4.494 4.502 0 2.481-2.01 4.498-4.494 4.498m-6.868-80.911h13.727c.556 0 1.01.383 1.01.852 0 .471-.452.854-1.01.854h-13.727c-.55 0-1.01-.383-1.01-.854-.0001-.469.454-.852 1.01-.852m-15.289 10.798c0-.794.675-1.438 1.508-1.438h41.29c.835 0 1.507.641 1.507 1.438v56.49c0 .791-.672 1.433-1.507 1.433h-41.29c-.833 0-1.508-.642-1.508-1.433v-56.49"/>
|
||||
</svg>
|
After Width: | Height: | Size: 702 B |
|
@ -558,6 +558,7 @@
|
|||
{
|
||||
"if": "payment:cash=yes",
|
||||
"ifnot": "payment:cash=no",
|
||||
"icon": "./assets/svg/cash.svg",
|
||||
"then": {
|
||||
"en": "Cash is accepted here",
|
||||
"nl": "Cash geld wordt hier aanvaard",
|
||||
|
@ -579,6 +580,7 @@
|
|||
{
|
||||
"if": "payment:cards=yes",
|
||||
"ifnot": "payment:cards=no",
|
||||
"icon": "./assets/svg/payment_card.svg",
|
||||
"then": {
|
||||
"en": "Payment cards are accepted here",
|
||||
"nl": "Betalen met bankkaarten kan hier",
|
||||
|
@ -599,6 +601,31 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"payment-options-advanced": {
|
||||
"builtin": "payment-options",
|
||||
"override": {
|
||||
"mappings+": [
|
||||
{
|
||||
"if": "payment:app=yes",
|
||||
"ifnot": "payment:app=no",
|
||||
"icon": "./assets/svg/smartphone.svg",
|
||||
"then": {
|
||||
"en": "Payment is done using a dedicated app",
|
||||
"nl": "Betalen via een app van het netwerk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "payment:membership_card=yes",
|
||||
"ifnot": "payment:membership_card=no",
|
||||
"icon": "./assets/svg/nfc_card.svg",
|
||||
"then": {
|
||||
"en": "Payment is done using a membership card",
|
||||
"nl": "Betalen via een lidkaart van het netwerk"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"last_edit": {
|
||||
"#": "Gives some metainfo about the last edit and who did edit it - rendering only",
|
||||
"condition": "_last_edit:contributor~*",
|
||||
|
|
|
@ -171,19 +171,36 @@
|
|||
{
|
||||
"if": "rental=city_bike",
|
||||
"then": {
|
||||
"en": "Normal city bikes can be rented here"
|
||||
"en": "Normal city bikes can be rented here",
|
||||
"nl": "Gewone stadsfietsen kunnen hier gehuurd worden"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "rental=ebike",
|
||||
"then": {
|
||||
"en": "Electrical bikes can be rented here"
|
||||
"en": "Electrical bikes can be rented here",
|
||||
"nl": "Elektrische fietsen kunnen hier gehuurd worden"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "rental=bmx",
|
||||
"then": {
|
||||
"en": "BMX bikes can be rented here"
|
||||
"en": "BMX bikes can be rented here",
|
||||
"nl": "BMX-fietsen kunnen hier gehuurd worden"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "rental=mtb",
|
||||
"then": {
|
||||
"en": "Mountainbikes can be rented here",
|
||||
"nl": "Mountainbikes kunnen hier gehuurd worden"
|
||||
}
|
||||
},
|
||||
{
|
||||
"if": "rental=kid_bike",
|
||||
"then": {
|
||||
"en": "Bikes for childs can be rented here",
|
||||
"nl": "Kinderfietsen kunnen hier gehuurd worden"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -192,26 +209,70 @@
|
|||
"rewrite": {
|
||||
"sourceString": [
|
||||
"bicycle_type",
|
||||
"bicycle_types"
|
||||
"type_plural"
|
||||
],
|
||||
"into": [
|
||||
[
|
||||
"city_bike",
|
||||
"city bikes"
|
||||
{
|
||||
"en": "city bikes",
|
||||
"nl": "Stadsfietsen"
|
||||
}
|
||||
],
|
||||
[
|
||||
"ebike",
|
||||
"electrical bikes"
|
||||
{
|
||||
"en": "electrical bikes",
|
||||
"nl": "elektrische fietsen"
|
||||
}
|
||||
],
|
||||
"bmx"
|
||||
[
|
||||
"kid_bike",
|
||||
{
|
||||
"en": "bikes for children",
|
||||
"nl": "Kinderfietsen"
|
||||
}
|
||||
],
|
||||
[
|
||||
"bmx",
|
||||
{
|
||||
"en": "BMX bikes",
|
||||
"nl": "BMX-fietsen"
|
||||
}
|
||||
],
|
||||
[
|
||||
"mtb",
|
||||
{
|
||||
"en": "mountainbike",
|
||||
"nl": "mountainbike"
|
||||
}
|
||||
],
|
||||
[
|
||||
"bicycle_pannier",
|
||||
{
|
||||
"en": "bicycle panniers",
|
||||
"nl": "Fietstassen"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"renderings": [
|
||||
{
|
||||
"id": "rental-capacity-bicycle-type",
|
||||
"group": "",
|
||||
"question": {
|
||||
"en": "How much bicycle_type ",
|
||||
"nl": "Hoeveel "
|
||||
}
|
||||
"en": "How much type_plural can be rented here? ",
|
||||
"nl": "Hoeveel type_plural kunnen hier uitgeleend worden?"
|
||||
},
|
||||
"render": {
|
||||
"en": "{capacity:bicycle_type} type_plural can be rented here",
|
||||
"nl": "{capacity:bicycle_type} type_plural kunnen hier uitgeleend worden"
|
||||
},
|
||||
"freeform": {
|
||||
"key": "capacity:bicycle_type",
|
||||
"type": "pnat"
|
||||
},
|
||||
"condition": "rental~.*bicycle_type.*"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -95,6 +95,10 @@
|
|||
"if": "theme=benches",
|
||||
"then": "./assets/themes/benches/bench_poi.svg"
|
||||
},
|
||||
{
|
||||
"if": "theme=bicycle_rental",
|
||||
"then": "./assets/themes/bicycle_rental/logo.svg"
|
||||
},
|
||||
{
|
||||
"if": "theme=bicyclelib",
|
||||
"then": "./assets/themes/bicyclelib/logo.svg"
|
||||
|
|
|
@ -62,10 +62,16 @@
|
|||
},
|
||||
{
|
||||
"rewrite": {
|
||||
"sourceString": "left|right",
|
||||
"sourceString": [
|
||||
"left|right"
|
||||
],
|
||||
"into": [
|
||||
"left",
|
||||
"right"
|
||||
[
|
||||
"left"
|
||||
],
|
||||
[
|
||||
"right"
|
||||
]
|
||||
]
|
||||
},
|
||||
"renderings": [
|
||||
|
|
|
@ -14,6 +14,8 @@ import Title from "../UI/Base/Title";
|
|||
import Minimap from "../UI/Base/Minimap";
|
||||
import {QueryParameters} from "../Logic/Web/QueryParameters";
|
||||
import QueryParameterDocumentation from "../UI/QueryParameterDocumentation";
|
||||
import ScriptUtils from "./ScriptUtils";
|
||||
import List from "../UI/Base/List";
|
||||
|
||||
function WriteFile(filename, html: BaseUIElement, autogenSource: string[]): void {
|
||||
|
||||
|
@ -50,6 +52,53 @@ WriteFile("./Docs/CalculatedTags.md", new Combine([new Title("Metatags", 1),
|
|||
WriteFile("./Docs/SpecialInputElements.md", ValidatedTextField.HelpText(), ["ValidatedTextField.ts"]);
|
||||
WriteFile("./Docs/BuiltinLayers.md", AllKnownLayouts.GenLayerOverviewText(), ["AllKnownLayers.ts"])
|
||||
|
||||
{
|
||||
var layers = ScriptUtils.getLayerFiles().map(f => f.parsed)
|
||||
var builtinsPerLayer= new Map<string, string[]>();
|
||||
var layersUsingBuiltin = new Map<string /* Builtin */, string[]>();
|
||||
for (const layer of layers) {
|
||||
if(layer.tagRenderings === undefined){
|
||||
continue
|
||||
}
|
||||
const usedBuiltins : string[] = []
|
||||
for (const tagRendering of layer.tagRenderings) {
|
||||
if(typeof tagRendering === "string"){
|
||||
usedBuiltins.push(tagRendering)
|
||||
continue
|
||||
}
|
||||
if(tagRendering["builtin"] !== undefined){
|
||||
const builtins = tagRendering["builtin"]
|
||||
if(typeof builtins === "string"){
|
||||
usedBuiltins.push(builtins)
|
||||
}else{
|
||||
usedBuiltins.push(...builtins)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const usedBuiltin of usedBuiltins) {
|
||||
var using = layersUsingBuiltin.get(usedBuiltin)
|
||||
if(using === undefined){
|
||||
layersUsingBuiltin.set(usedBuiltin, [layer.id])
|
||||
}else{
|
||||
using.push(layer.id)
|
||||
}
|
||||
}
|
||||
|
||||
builtinsPerLayer.set(layer.id, usedBuiltins)
|
||||
}
|
||||
|
||||
const docs = new Combine([
|
||||
new Title("Index of builtin TagRendering" ,1),
|
||||
new Title("Existing builtin tagrenderings", 2),
|
||||
... Array.from(layersUsingBuiltin.entries()).map(([builtin, usedByLayers]) =>
|
||||
new Combine([
|
||||
new Title(builtin),
|
||||
new List(usedByLayers)
|
||||
]).SetClass("flex flex-col")
|
||||
)
|
||||
]).SetClass("flex flex-col")
|
||||
WriteFile("./Docs/BuiltinIndex.md", docs, ["assets/layers/*.json"])
|
||||
}
|
||||
|
||||
Minimap.createMiniMap = _ => {
|
||||
console.log("Not creating a minimap, it is disabled");
|
||||
|
@ -58,12 +107,12 @@ Minimap.createMiniMap = _ => {
|
|||
|
||||
|
||||
const dummyLayout = new LayoutConfig({
|
||||
language: ["en"],
|
||||
id: ">theme<",
|
||||
maintainer: "pietervdvn",
|
||||
version: "0",
|
||||
title: "<theme>",
|
||||
title: {en:"<theme>"},
|
||||
description: "A theme to generate docs with",
|
||||
socialImage: "./assets/SocialImage.png",
|
||||
startLat: 0,
|
||||
startLon: 0,
|
||||
startZoom: 0,
|
||||
|
|
|
@ -228,7 +228,7 @@ function compileTranslationsFromWeblate() {
|
|||
* @param objects
|
||||
* @param target
|
||||
*/
|
||||
function generateTranslationsObjectFrom(objects: { path: string, parsed: { id: string } }[], target: string) {
|
||||
function generateTranslationsObjectFrom(objects: { path: string, parsed: { id: string } }[], target: string): string[] {
|
||||
const tr = new TranslationPart();
|
||||
|
||||
for (const layerFile of objects) {
|
||||
|
@ -257,6 +257,7 @@ function generateTranslationsObjectFrom(objects: { path: string, parsed: { id: s
|
|||
|
||||
writeFileSync(`langs/${target}/${lang}.json`, json)
|
||||
}
|
||||
return langs
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -398,11 +399,14 @@ if (!themeOverwritesWeblate) {
|
|||
} else {
|
||||
console.log("Ignore weblate")
|
||||
}
|
||||
generateTranslationsObjectFrom(ScriptUtils.getLayerFiles(), "layers")
|
||||
generateTranslationsObjectFrom(ScriptUtils.getThemeFiles().filter(th => th.parsed.mustHaveLanguage === undefined), "themes")
|
||||
|
||||
const l1 = generateTranslationsObjectFrom(ScriptUtils.getLayerFiles(), "layers")
|
||||
const l2 = generateTranslationsObjectFrom(ScriptUtils.getThemeFiles().filter(th => th.parsed.mustHaveLanguage === undefined), "themes")
|
||||
const l3 = generateTranslationsObjectFrom([{path: questionsPath, parsed: questionsParsed}], "shared-questions")
|
||||
|
||||
generateTranslationsObjectFrom([{path: questionsPath, parsed: questionsParsed}], "shared-questions")
|
||||
const usedLanguages = Utils.Dedup(l1.concat(l2).concat(l3)).filter(v => v !== "*")
|
||||
usedLanguages.sort()
|
||||
fs.writeFileSync("./assets/generated/used_languages.json", JSON.stringify({languages: usedLanguages}))
|
||||
|
||||
if (!themeOverwritesWeblate) {
|
||||
// Generates the core translations
|
||||
|
|
Loading…
Reference in a new issue