diff --git a/Models/ThemeConfig/Conversion/FixImages.ts b/Models/ThemeConfig/Conversion/FixImages.ts index 0b8bb1de0..aa463fa73 100644 --- a/Models/ThemeConfig/Conversion/FixImages.ts +++ b/Models/ThemeConfig/Conversion/FixImages.ts @@ -2,64 +2,89 @@ import {Conversion, DesugaringStep} from "./Conversion"; import {LayoutConfigJson} from "../Json/LayoutConfigJson"; import {Utils} from "../../../Utils"; import * as metapaths from "../../../assets/layoutconfigmeta.json"; -import * as tagrenderingmetapaths from "../../../assets/tagrenderingconfigmeta.json"; +import * as tagrenderingmetapaths from "../../../assets/questionabletagrenderingconfigmeta.json"; +import Translations from "../../../UI/i18n/Translations"; export class ExtractImages extends Conversion { private _isOfficial: boolean; private _sharedTagRenderings: Map; - private static readonly layoutMetaPaths = (metapaths["default"] ?? metapaths).filter(mp => mp.typeHint !== undefined && (mp.typeHint === "image" || mp.typeHint === "icon")) - private static readonly tagRenderingMetaPaths = (tagrenderingmetapaths["default"] ?? tagrenderingmetapaths).filter(trpath => trpath.typeHint === "rendered") + private static readonly layoutMetaPaths = (metapaths["default"] ?? metapaths) + .filter(mp => (ExtractImages.mightBeTagRendering(mp)) || mp.typeHint !== undefined && (mp.typeHint === "image" || mp.typeHint === "icon")) + private static readonly tagRenderingMetaPaths = (tagrenderingmetapaths["default"] ?? tagrenderingmetapaths) constructor(isOfficial: boolean, sharedTagRenderings: Map) { - super("Extract all images from a layoutConfig using the meta paths",[],"ExctractImages"); + super("Extract all images from a layoutConfig using the meta paths.",[],"ExctractImages"); this._isOfficial = isOfficial; this._sharedTagRenderings = sharedTagRenderings; } + + public static mightBeTagRendering(metapath: {type: string | string[]}) : boolean{ + if(!Array.isArray(metapath.type)){ + return false + } + return metapath.type.some(t => + t["$ref"] == "#/definitions/TagRenderingConfigJson" || t["$ref"] == "#/definitions/QuestionableTagRenderingConfigJson") + } convert(json: LayoutConfigJson, context: string): { result: string[], errors: string[], warnings: string[] } { const allFoundImages : string[] = [] const errors = [] const warnings = [] for (const metapath of ExtractImages.layoutMetaPaths) { - const mightBeTr = Array.isArray(metapath.type) && metapath.type.some(t => t["$ref"] == "#/definitions/TagRenderingConfigJson") + const mightBeTr = ExtractImages.mightBeTagRendering(metapath) + const allRenderedValuesAreImages = metapath.typeHint === "icon" || metapath.typeHint === "image" const found = Utils.CollectPath(metapath.path, json) if (mightBeTr) { // We might have tagRenderingConfigs containing icons here for (const el of found) { const path = el.path const foundImage = el.leaf; - if (typeof foundImage === "string") { + if (typeof foundImage === "string") { + if(!allRenderedValuesAreImages){ + continue + } + if(foundImage == ""){ warnings.push(context+"."+path.join(".")+" Found an empty image") } if(this._sharedTagRenderings?.has(foundImage)){ // This is not an image, but a shared tag rendering + // At key positions for checking, they'll be expanded already, so we can safely ignore them here continue } allFoundImages.push(foundImage) } else{ - // This is a tagRendering where every rendered value might be an icon! + // This is a tagRendering. + // Either every rendered value might be an icon + // or -in the case of a normal tagrendering- only the 'icons' in the mappings have an icon (or exceptionally an '' tag in the translation for (const trpath of ExtractImages.tagRenderingMetaPaths) { + // Inspect all the rendered values const fromPath = Utils.CollectPath(trpath.path, foundImage) + const isRendered = trpath.typeHint === "rendered" + const isImage = trpath.typeHint === "icon" || trpath.typeHint === "image" for (const img of fromPath) { - if (typeof img.leaf !== "string") { - (this._isOfficial ? errors: warnings).push(context+"."+img.path.join(".")+": found an image path that is not a string: " + JSON.stringify(img.leaf)) - } - } - allFoundImages.push(...fromPath.map(i => i.leaf).filter(i => typeof i=== "string")) - for (const pathAndImg of fromPath) { - if(pathAndImg.leaf === "" || pathAndImg.leaf["path"] == ""){ - warnings.push(context+[...path,...pathAndImg.path].join(".")+": Found an empty image at ") + if (allRenderedValuesAreImages && isRendered) { + // What we found is an image + if(img.leaf === "" || img.leaf["path"] == ""){ + warnings.push(context+[...path,...img.path].join(".")+": Found an empty image at ") + }else if(typeof img.leaf !== "string"){ + (this._isOfficial ? errors: warnings).push(context+"."+img.path.join(".")+": found an image path that is not a string: " + JSON.stringify(img.leaf)) + }else{ + allFoundImages.push(img.leaf) + } + } + if(!allRenderedValuesAreImages && isImage){ + // Extract images from the translations + allFoundImages.push(...(Translations.T(img.leaf, "extract_images from "+img.path.join(".")).ExtractImages(false))) } } } - - } + } } } else { for (const foundElement of found) { @@ -127,7 +152,7 @@ export class FixImages extends DesugaringStep { if (metapath.typeHint !== "image" && metapath.typeHint !== "icon") { continue } - const mightBeTr = Array.isArray(metapath.type) && metapath.type.some(t => t["$ref"] == "#/definitions/TagRenderingConfigJson") + const mightBeTr = ExtractImages.mightBeTagRendering(metapath) Utils.WalkPath(metapath.path, json, (leaf, path) => { if (typeof leaf === "string") { return replaceString(leaf) diff --git a/Models/ThemeConfig/Conversion/Validation.ts b/Models/ThemeConfig/Conversion/Validation.ts index f8174c5b0..349fa2c34 100644 --- a/Models/ThemeConfig/Conversion/Validation.ts +++ b/Models/ThemeConfig/Conversion/Validation.ts @@ -13,6 +13,7 @@ import ScriptUtils from "../../../scripts/ScriptUtils"; import {And} from "../../../Logic/Tags/And"; import Translations from "../../../UI/i18n/Translations"; import Svg from "../../../Svg"; +import {QuestionableTagRenderingConfigJson} from "../Json/QuestionableTagRenderingConfigJson"; class ValidateLanguageCompleteness extends DesugaringStep { @@ -237,12 +238,12 @@ export class PrevalidateTheme extends Fuse { } -export class DetectShadowedMappings extends DesugaringStep { +export class DetectShadowedMappings extends DesugaringStep { constructor() { super("Checks that the mappings don't shadow each other", [], "DetectShadowedMappings"); } - convert(json: TagRenderingConfigJson, context: string): { result: TagRenderingConfigJson; errors?: string[]; warnings?: string[] } { + convert(json: QuestionableTagRenderingConfigJson, context: string): { result: QuestionableTagRenderingConfigJson; errors?: string[]; warnings?: string[] } { const errors = [] const warnings = [] if (json.mappings === undefined || json.mappings.length === 0) { diff --git a/Models/ThemeConfig/Json/LayerConfigJson.ts b/Models/ThemeConfig/Json/LayerConfigJson.ts index 5c32c1986..dce00b7c0 100644 --- a/Models/ThemeConfig/Json/LayerConfigJson.ts +++ b/Models/ThemeConfig/Json/LayerConfigJson.ts @@ -6,6 +6,8 @@ import UnitConfigJson from "./UnitConfigJson"; import MoveConfigJson from "./MoveConfigJson"; import PointRenderingConfigJson from "./PointRenderingConfigJson"; import LineRenderingConfigJson from "./LineRenderingConfigJson"; +import {QuestionableTagRenderingConfigJson} from "./QuestionableTagRenderingConfigJson"; +import RewritableConfigJson from "./RewritableConfigJson"; /** * Configuration for a single layer @@ -40,7 +42,7 @@ export interface LayerConfigJson { * Every source _must_ define which tags _must_ be present in order to be picked up. * */ - source: + source: ({ /** * Every source must set which tags have to be present in order to load the given layer. @@ -58,40 +60,40 @@ export interface LayerConfigJson { */ overpassScript?: string } | - { - /** - * The actual source of the data to load, if loaded via geojson. - * - * # A single geojson-file - * source: {geoJson: "https://my.source.net/some-geo-data.geojson"} - * fetches a geojson from a third party source - * - * # A tiled geojson source - * source: {geoJson: "https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson", geoJsonZoomLevel: 14} - * to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer - * - * Some API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max} - */ - geoJson: string, - /** - * To load a tiled geojson layer, set the zoomlevel of the tiles - */ - geoJsonZoomLevel?: number, - /** - * Indicates that the upstream geojson data is OSM-derived. - * Useful for e.g. merging or for scripts generating this cache - */ - isOsmCache?: boolean, - /** - * Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this - */ - mercatorCrs?: boolean, - /** - * Some API's have an id-field, but give it a different name. - * Setting this key will rename this field into 'id' - */ - idKey?: string - }) + { + /** + * The actual source of the data to load, if loaded via geojson. + * + * # A single geojson-file + * source: {geoJson: "https://my.source.net/some-geo-data.geojson"} + * fetches a geojson from a third party source + * + * # A tiled geojson source + * source: {geoJson: "https://my.source.net/some-tile-geojson-{layer}-{z}-{x}-{y}.geojson", geoJsonZoomLevel: 14} + * to use a tiled geojson source. The web server must offer multiple geojsons. {z}, {x} and {y} are substituted by the location; {layer} is substituted with the id of the loaded layer + * + * Some API's use a BBOX instead of a tile, this can be used by specifying {y_min}, {y_max}, {x_min} and {x_max} + */ + geoJson: string, + /** + * To load a tiled geojson layer, set the zoomlevel of the tiles + */ + geoJsonZoomLevel?: number, + /** + * Indicates that the upstream geojson data is OSM-derived. + * Useful for e.g. merging or for scripts generating this cache + */ + isOsmCache?: boolean, + /** + * Some API's use a mercator-projection (EPSG:900913) instead of WGS84. Set the flag `mercatorCrs: true` in the source for this + */ + mercatorCrs?: boolean, + /** + * Some API's have an id-field, but give it a different name. + * Setting this key will rename this field into 'id' + */ + idKey?: string + }) /** * @@ -174,7 +176,9 @@ export interface LayerConfigJson { */ titleIcons?: (string | TagRenderingConfigJson)[] | ["defaults"]; - + /** + * Visualisation of the items on the map + */ mapRendering: null | (PointRenderingConfigJson | LineRenderingConfigJson)[] /** @@ -260,13 +264,12 @@ export interface LayerConfigJson { * This is mainly create questions for a 'left' and a 'right' side of the road. * These will be grouped and questions will be asked together */ - tagRenderings?: (string | { builtin: string, override: any } | TagRenderingConfigJson | { - rewrite: { - sourceString: string[], - into: (string | any)[][] - }, - renderings: (string | { builtin: string, override: any } | TagRenderingConfigJson)[] - }) [], + tagRenderings?: + (string + | { builtin: string, override: any } + | QuestionableTagRenderingConfigJson + | RewritableConfigJson<(string | { builtin: string, override: any } | QuestionableTagRenderingConfigJson)[]> + ) [], /** @@ -401,7 +404,7 @@ export interface LayerConfigJson { /** * If set, synchronizes wether or not this layer is selected. - * + * * no: Do not sync at all, always revert to default * local: keep selection on local storage * theme-only: sync via OSM, but this layer will only be toggled in this theme diff --git a/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts b/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts index 0b08218b5..33c871646 100644 --- a/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson.ts @@ -1,51 +1,18 @@ import {AndOrTagConfigJson} from "./TagConfigJson"; +import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; /** - * A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet. + * A QuestionableTagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet. * If the desired tags are missing and a question is defined, a question will be shown instead. */ -export interface TagRenderingConfigJson { +export interface QuestionableTagRenderingConfigJson extends TagRenderingConfigJson { - /** - * The id of the tagrendering, should be an unique string. - * Used to keep the translations in sync. Only used in the tagRenderings-array of a layerConfig, not requered otherwise. - * - * Use 'questions' to trigger the question box of this group (if a group is defined) - */ - id?: string, - - /** - * If 'group' is defined on many tagRenderings, these are grouped together when shown. The questions are grouped together as well. - * The first tagRendering of a group will always be a sticky element. - */ - group?: string - - /** - * A list of labels. These are strings that are used for various purposes, e.g. to filter them away - */ - labels?: string[] - - /** - * Renders this value. Note that "{key}"-parts are substituted by the corresponding values of the element. - * If neither 'textFieldQuestion' nor 'mappings' are defined, this text is simply shown as default value. - * - * Note that this is a HTML-interpreted value, so you can add links as e.g. '{website}' or include images such as `This is of type A
` - * type: rendered - */ - render?: string | any, - /** * If it turns out that this tagRendering doesn't match _any_ value, then we show this question. * If undefined, the question is never asked and this tagrendering is read-only */ question?: string | any, - /** - * Only show this question if the object also matches the following tags. - * - * This is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables... - * */ - condition?: AndOrTagConfigJson | string; /** * Allow freeform text input from the user @@ -53,10 +20,10 @@ export interface TagRenderingConfigJson { freeform?: { /** - * If this key is present, then 'render' is used to display the value. - * If this is undefined, the rendering is _always_ shown + * @inheritDoc */ - key: string, + key: string + /** * The type of the text-field, e.g. 'string', 'nat', 'float', 'date',... * See Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values @@ -66,7 +33,7 @@ export interface TagRenderingConfigJson { * A (translated) text that is shown (as gray text) within the textfield */ placeholder?: string | any - + /** * Extra parameters to initialize the input helper arguments. * For semantics, see the 'SpecialInputElements.md' @@ -104,36 +71,30 @@ export interface TagRenderingConfigJson { mappings?: { /** - * If this condition is met, then the text under `then` will be shown. - * If no value matches, and the user selects this mapping as an option, then these tags will be uploaded to OSM. - * - * For example: {'if': 'diet:vegetarion=yes', 'then':'A vegetarian option is offered here'} - * - * This can be an substituting-tag as well, e.g. {'if': 'addr:street:={_calculated_nearby_streetname}', 'then': '{_calculated_nearby_streetname}'} + * @inheritDoc */ if: AndOrTagConfigJson | string, /** - * If the condition `if` is met, the text `then` will be rendered. - * If not known yet, the user will be presented with `then` as an option + * Shown if the 'if is fulfilled * Type: rendered */ then: string | any, /** - * An icon supporting this mapping; typically shown pretty small + * An extra icon supporting the choice * Type: icon */ icon?: string | { /** - * The path to the icon + * The path to the icon * Type: icon */ path: string, /** - * A hint to mapcomplete on how to render this icon within the mapping. - * This is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged) + * Size of the image */ class: "small" | "medium" | "large" | string } + /** * In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation). * diff --git a/Models/ThemeConfig/Json/RewritableConfigJson.ts b/Models/ThemeConfig/Json/RewritableConfigJson.ts index e69de29bb..23fa11b6a 100644 --- a/Models/ThemeConfig/Json/RewritableConfigJson.ts +++ b/Models/ThemeConfig/Json/RewritableConfigJson.ts @@ -0,0 +1,9 @@ +import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; + +export default interface RewritableConfigJson { + rewrite: { + sourceString: string[], + into: (string | any)[][] + }, + renderings: T +} \ No newline at end of file diff --git a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts index 0b08218b5..2938083ba 100644 --- a/Models/ThemeConfig/Json/TagRenderingConfigJson.ts +++ b/Models/ThemeConfig/Json/TagRenderingConfigJson.ts @@ -2,7 +2,7 @@ import {AndOrTagConfigJson} from "./TagConfigJson"; /** * A TagRenderingConfigJson is a single piece of code which converts one ore more tags into a HTML-snippet. - * If the desired tags are missing and a question is defined, a question will be shown instead. + * For an _editable_ tagRenerdering, use 'QuestionableTagRenderingConfigJson' instead, which extends this one */ export interface TagRenderingConfigJson { @@ -35,13 +35,7 @@ export interface TagRenderingConfigJson { render?: string | any, /** - * If it turns out that this tagRendering doesn't match _any_ value, then we show this question. - * If undefined, the question is never asked and this tagrendering is read-only - */ - question?: string | any, - - /** - * Only show this question if the object also matches the following tags. + * Only show this tagrendering (or question) if the object also matches the following tags. * * This is useful to ask a follow-up question. E.g. if there is a diaper table, then ask a follow-up question on diaper tables... * */ @@ -57,47 +51,8 @@ export interface TagRenderingConfigJson { * If this is undefined, the rendering is _always_ shown */ key: string, - /** - * The type of the text-field, e.g. 'string', 'nat', 'float', 'date',... - * See Docs/SpecialInputElements.md and UI/Input/ValidatedTextField.ts for supported values - */ - type?: string, - /** - * A (translated) text that is shown (as gray text) within the textfield - */ - placeholder?: string | any - - /** - * Extra parameters to initialize the input helper arguments. - * For semantics, see the 'SpecialInputElements.md' - */ - helperArgs?: (string | number | boolean | any)[]; - /** - * If a value is added with the textfield, these extra tag is addded. - * Useful to add a 'fixme=freeform textfield used - to be checked' - **/ - addExtraTags?: string[]; - - /** - * When set, influences the way a question is asked. - * Instead of showing a full-widht text field, the text field will be shown within the rendering of the question. - * - * This combines badly with special input elements, as it'll distort the layout. - */ - inline?: boolean - - /** - * default value to enter if no previous tagging is present. - * Normally undefined (aka do not enter anything) - */ - default?: string }, - /** - * If true, use checkboxes instead of radio buttons when asking the question - */ - multiAnswer?: boolean, - /** * Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes */ @@ -134,82 +89,6 @@ export interface TagRenderingConfigJson { */ class: "small" | "medium" | "large" | string } - /** - * In some cases, multiple taggings exist (e.g. a default assumption, or a commonly mapped abbreviation and a fully written variation). - * - * In the latter case, a correct text should be shown, but only a single, canonical tagging should be selectable by the user. - * In this case, one of the mappings can be hiden by setting this flag. - * - * To demonstrate an example making a default assumption: - * - * mappings: [ - * { - * if: "access=", -- no access tag present, we assume accessible - * then: "Accessible to the general public", - * hideInAnswer: true - * }, - * { - * if: "access=yes", - * then: "Accessible to the general public", -- the user selected this, we add that to OSM - * }, - * { - * if: "access=no", - * then: "Not accessible to the public" - * } - * ] - * - * - * For example, for an operator, we have `operator=Agentschap Natuur en Bos`, which is often abbreviated to `operator=ANB`. - * Then, we would add two mappings: - * { - * if: "operator=Agentschap Natuur en Bos" -- the non-abbreviated version which should be uploaded - * then: "Maintained by Agentschap Natuur en Bos" - * }, - * { - * if: "operator=ANB", -- we don't want to upload abbreviations - * then: "Maintained by Agentschap Natuur en Bos" - * hideInAnswer: true - * } - * - * Hide in answer can also be a tagsfilter, e.g. to make sure an option is only shown when appropriate. - * Keep in mind that this is reverse logic: it will be hidden in the answer if the condition is true, it will thus only show in the case of a mismatch - * - * e.g., for toilets: if "wheelchair=no", we know there is no wheelchair dedicated room. - * For the location of the changing table, the option "in the wheelchair accessible toilet is weird", so we write: - * - * { - * "question": "Where is the changing table located?" - * "mappings": [ - * {"if":"changing_table:location=female","then":"In the female restroom"}, - * {"if":"changing_table:location=male","then":"In the male restroom"}, - * {"if":"changing_table:location=wheelchair","then":"In the wheelchair accessible restroom", "hideInAnswer": "wheelchair=no"}, - * - * ] - * } - * - * Also have a look for the meta-tags - * { - * if: "operator=Agentschap Natuur en Bos", - * then: "Maintained by Agentschap Natuur en Bos", - * hideInAnswer: "_country!=be" - * } - */ - hideInAnswer?: boolean | string | AndOrTagConfigJson, - /** - * Only applicable if 'multiAnswer' is set. - * This is for situations such as: - * `accepts:coins=no` where one can select all the possible payment methods. However, we want to make explicit that some options _were not_ selected. - * This can be done with `ifnot` - * Note that we can not explicitly render this negative case to the user, we cannot show `does _not_ accept coins`. - * If this is important to your usecase, consider using multiple radiobutton-fields without `multiAnswer` - */ - ifnot?: AndOrTagConfigJson | string - - /** - * If chosen as answer, these tags will be applied as well onto the object. - * Not compatible with multiAnswer - */ - addExtraTags?: string[] }[] } \ No newline at end of file diff --git a/Models/ThemeConfig/TagRenderingConfig.ts b/Models/ThemeConfig/TagRenderingConfig.ts index f0e797aae..81a692862 100644 --- a/Models/ThemeConfig/TagRenderingConfig.ts +++ b/Models/ThemeConfig/TagRenderingConfig.ts @@ -1,6 +1,5 @@ import {Translation} from "../../UI/i18n/Translation"; import {TagsFilter} from "../../Logic/Tags/TagsFilter"; -import {TagRenderingConfigJson} from "./Json/TagRenderingConfigJson"; import Translations from "../../UI/i18n/Translations"; import {TagUtils} from "../../Logic/Tags/TagUtils"; import {And} from "../../Logic/Tags/And"; @@ -12,6 +11,7 @@ import Combine from "../../UI/Base/Combine"; import Title from "../../UI/Base/Title"; import Link from "../../UI/Base/Link"; import List from "../../UI/Base/List"; +import {QuestionableTagRenderingConfigJson} from "./Json/QuestionableTagRenderingConfigJson"; /*** * The parsed version of TagRenderingConfigJSON @@ -50,7 +50,7 @@ export default class TagRenderingConfig { }[] public readonly labels: string[] - constructor(json: string | TagRenderingConfigJson, context?: string) { + constructor(json: string | QuestionableTagRenderingConfigJson, context?: string) { if (json === undefined) { throw "Initing a TagRenderingConfig with undefined in " + context; } diff --git a/assets/layoutconfigmeta.json b/assets/layoutconfigmeta.json index c596f78b9..0e9b3bc51 100644 --- a/assets/layoutconfigmeta.json +++ b/assets/layoutconfigmeta.json @@ -166,7 +166,44 @@ "path": [ "layers" ], - "type": "array" + "type": [ + { + "$ref": "#/definitions/LayerConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "override": {}, + "hideTagRenderingsWithLabels": { + "description": "TagRenderings with any of these labels will be removed from the layer.\nNote that the 'id' and 'group' are considered labels too", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "builtin", + "override" + ] + }, + { + "type": "string" + } + ] }, { "path": [ @@ -344,13 +381,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "isShown", - "question" - ] - }, { "path": [ "layers", @@ -383,67 +413,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "isShown", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "isShown", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "isShown", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "isShown", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "isShown", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "isShown", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "isShown", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -529,50 +498,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "isShown", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "isShown", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "isShown", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -654,13 +579,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "title", - "question" - ] - }, { "path": [ "layers", @@ -693,67 +611,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "title", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "title", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "title", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "title", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "title", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "title", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "title", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -839,50 +696,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "title", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "title", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "title", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -957,13 +770,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "titleIcons", - "question" - ] - }, { "path": [ "layers", @@ -996,67 +802,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "titleIcons", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "titleIcons", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "titleIcons", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "titleIcons", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "titleIcons", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "titleIcons", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "titleIcons", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -1145,47 +890,27 @@ { "path": [ "layers", - "titleIcons", - "mappings", - "hideInAnswer" + "mapRendering" ], "type": [ { - "$ref": "#/definitions/AndOrTagConfigJson" + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/definitions/default_4" + }, + { + "$ref": "#/definitions/default_5" + } + ] + } }, { - "type": [ - "string", - "boolean" - ] + "type": "null" } ] }, - { - "path": [ - "layers", - "titleIcons", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "titleIcons", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -1261,14 +986,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "question" - ] - }, { "path": [ "layers", @@ -1304,74 +1021,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -1463,53 +1112,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "icon", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -1584,15 +1186,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "question" - ] - }, { "path": [ "layers", @@ -1631,81 +1224,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -1803,56 +1321,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "iconBadges", - "then", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -1912,14 +1380,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "question" - ] - }, { "path": [ "layers", @@ -1955,74 +1415,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -2114,53 +1506,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "iconSize", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -2220,14 +1565,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "question" - ] - }, { "path": [ "layers", @@ -2263,74 +1600,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -2422,53 +1691,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "rotation", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -2528,14 +1750,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "label", - "question" - ] - }, { "path": [ "layers", @@ -2571,74 +1785,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "label", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -2730,53 +1876,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "label", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "label", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -2843,14 +1942,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "color", - "question" - ] - }, { "path": [ "layers", @@ -2886,74 +1977,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "color", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -3045,53 +2068,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "color", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "color", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -3154,14 +2130,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "width", - "question" - ] - }, { "path": [ "layers", @@ -3197,74 +2165,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "width", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -3356,53 +2256,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "width", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "width", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -3462,14 +2315,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "question" - ] - }, { "path": [ "layers", @@ -3505,74 +2350,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -3664,53 +2441,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "dashArray", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -3770,14 +2500,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "question" - ] - }, { "path": [ "layers", @@ -3813,74 +2535,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -3972,53 +2626,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "lineCap", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -4082,14 +2689,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "question" - ] - }, { "path": [ "layers", @@ -4125,74 +2724,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -4284,53 +2815,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "fill", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -4390,14 +2874,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "question" - ] - }, { "path": [ "layers", @@ -4433,74 +2909,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -4592,53 +3000,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "fillColor", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -4698,14 +3059,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "question" - ] - }, { "path": [ "layers", @@ -4741,74 +3094,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "freeform", - "placeholder" - ] - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "layers", @@ -4900,53 +3185,6 @@ ], "type": "string" }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "layers", - "mapRendering", - "offset", - "mappings", - "addExtraTags" - ], - "type": "array" - }, { "path": [ "layers", @@ -5099,7 +3337,30 @@ "layers", "tagRenderings" ], - "type": "array" + "type": [ + { + "$ref": "#/definitions/QuestionableTagRenderingConfigJson" + }, + { + "type": "object", + "properties": { + "builtin": { + "type": "string" + }, + "override": {} + }, + "required": [ + "builtin", + "override" + ] + }, + { + "$ref": "#/definitions/default<(string|QuestionableTagRenderingConfigJson|{builtin:string;override:any;})[]>" + }, + { + "type": "string" + } + ] }, { "path": [ @@ -5108,38 +3369,6 @@ ], "type": "object" }, - { - "path": [ - "layers", - "tagRenderings", - "id" - ], - "type": "string" - }, - { - "path": [ - "layers", - "tagRenderings", - "group" - ], - "type": "string" - }, - { - "path": [ - "layers", - "tagRenderings", - "labels" - ], - "type": "array" - }, - { - "path": [ - "layers", - "tagRenderings", - "render" - ], - "typeHint": "rendered" - }, { "path": [ "layers", @@ -5147,21 +3376,6 @@ "question" ] }, - { - "path": [ - "layers", - "tagRenderings", - "condition" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, { "path": [ "layers", @@ -5170,15 +3384,6 @@ ], "type": "object" }, - { - "path": [ - "layers", - "tagRenderings", - "freeform", - "key" - ], - "type": "string" - }, { "path": [ "layers", @@ -5248,22 +3453,6 @@ ], "type": "array" }, - { - "path": [ - "layers", - "tagRenderings", - "mappings", - "if" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, { "path": [ "layers", @@ -5286,11 +3475,11 @@ "type": "object", "properties": { "path": { - "description": "The path to the icon\nType: icon", + "description": "The path to the icon\nType: icon", "type": "string" }, "class": { - "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "description": "Size of the image", "type": "string" } }, @@ -5369,6 +3558,53 @@ ], "type": "array" }, + { + "path": [ + "layers", + "tagRenderings", + "id" + ], + "type": "string" + }, + { + "path": [ + "layers", + "tagRenderings", + "group" + ], + "type": "string" + }, + { + "path": [ + "layers", + "tagRenderings", + "labels" + ], + "type": "array" + }, + { + "path": [ + "layers", + "tagRenderings", + "render" + ], + "typeHint": "rendered" + }, + { + "path": [ + "layers", + "tagRenderings", + "condition" + ], + "type": [ + { + "$ref": "#/definitions/AndOrTagConfigJson" + }, + { + "type": "string" + } + ] + }, { "path": [ "layers", @@ -5377,42 +3613,6 @@ ], "type": "object" }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "id" - ], - "type": "string" - }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "group" - ], - "type": "string" - }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "labels" - ], - "type": "array" - }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "render" - ], - "typeHint": "rendered" - }, { "path": [ "layers", @@ -5421,22 +3621,6 @@ "question" ] }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "condition" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, { "path": [ "layers", @@ -5446,16 +3630,6 @@ ], "type": "object" }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "freeform", - "key" - ], - "type": "string" - }, { "path": [ "layers", @@ -5533,23 +3707,6 @@ ], "type": "array" }, - { - "path": [ - "layers", - "tagRenderings", - "renderings", - "mappings", - "if" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, { "path": [ "layers", @@ -5574,11 +3731,11 @@ "type": "object", "properties": { "path": { - "description": "The path to the icon\nType: icon", + "description": "The path to the icon\nType: icon", "type": "string" }, "class": { - "description": "A hint to mapcomplete on how to render this icon within the mapping.\nThis is translated to 'mapping-icon-', so defining your own in combination with a custom CSS is possible (but discouraged)", + "description": "Size of the image", "type": "string" } }, @@ -5662,6 +3819,58 @@ ], "type": "array" }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "id" + ], + "type": "string" + }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "group" + ], + "type": "string" + }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "labels" + ], + "type": "array" + }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "render" + ], + "typeHint": "rendered" + }, + { + "path": [ + "layers", + "tagRenderings", + "renderings", + "condition" + ], + "type": [ + { + "$ref": "#/definitions/AndOrTagConfigJson" + }, + { + "type": "string" + } + ] + }, { "path": [ "layers", diff --git a/assets/questionabletagrenderingconfigmeta.json b/assets/questionabletagrenderingconfigmeta.json new file mode 100644 index 000000000..665bfaeab --- /dev/null +++ b/assets/questionabletagrenderingconfigmeta.json @@ -0,0 +1,198 @@ +[ + { + "path": [], + "type": "object" + }, + { + "path": [ + "question" + ] + }, + { + "path": [ + "freeform" + ], + "type": "object" + }, + { + "path": [ + "freeform", + "type" + ], + "type": "string" + }, + { + "path": [ + "freeform", + "placeholder" + ] + }, + { + "path": [ + "freeform", + "helperArgs" + ], + "type": "array" + }, + { + "path": [ + "freeform", + "addExtraTags" + ], + "type": "array" + }, + { + "path": [ + "freeform", + "inline" + ], + "type": "boolean" + }, + { + "path": [ + "freeform", + "default" + ], + "type": "string" + }, + { + "path": [ + "multiAnswer" + ], + "type": "boolean" + }, + { + "path": [ + "mappings" + ], + "type": "array" + }, + { + "path": [ + "mappings", + "then" + ], + "typeHint": "rendered" + }, + { + "path": [ + "mappings", + "icon" + ], + "typeHint": "icon", + "type": [ + { + "type": "object", + "properties": { + "path": { + "description": "The path to the icon\nType: icon", + "type": "string" + }, + "class": { + "description": "Size of the image", + "type": "string" + } + }, + "required": [ + "class", + "path" + ] + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "mappings", + "icon", + "path" + ], + "typeHint": "icon", + "type": "string" + }, + { + "path": [ + "mappings", + "icon", + "class" + ], + "type": "string" + }, + { + "path": [ + "mappings", + "hideInAnswer" + ], + "type": [ + { + "$ref": "#/definitions/AndOrTagConfigJson" + }, + { + "type": [ + "string", + "boolean" + ] + } + ] + }, + { + "path": [ + "mappings", + "ifnot" + ], + "type": [ + { + "$ref": "#/definitions/AndOrTagConfigJson" + }, + { + "type": "string" + } + ] + }, + { + "path": [ + "mappings", + "addExtraTags" + ], + "type": "array" + }, + { + "path": [ + "id" + ], + "type": "string" + }, + { + "path": [ + "group" + ], + "type": "string" + }, + { + "path": [ + "labels" + ], + "type": "array" + }, + { + "path": [ + "render" + ], + "typeHint": "rendered" + }, + { + "path": [ + "condition" + ], + "type": [ + { + "$ref": "#/definitions/AndOrTagConfigJson" + }, + { + "type": "string" + } + ] + } +] \ No newline at end of file diff --git a/assets/tagrenderingconfigmeta.json b/assets/tagrenderingconfigmeta.json index 6f7b349c1..5feb1da54 100644 --- a/assets/tagrenderingconfigmeta.json +++ b/assets/tagrenderingconfigmeta.json @@ -27,11 +27,6 @@ ], "typeHint": "rendered" }, - { - "path": [ - "question" - ] - }, { "path": [ "condition" @@ -58,53 +53,6 @@ ], "type": "string" }, - { - "path": [ - "freeform", - "type" - ], - "type": "string" - }, - { - "path": [ - "freeform", - "placeholder" - ] - }, - { - "path": [ - "freeform", - "helperArgs" - ], - "type": "array" - }, - { - "path": [ - "freeform", - "addExtraTags" - ], - "type": "array" - }, - { - "path": [ - "freeform", - "inline" - ], - "type": "boolean" - }, - { - "path": [ - "freeform", - "default" - ], - "type": "string" - }, - { - "path": [ - "multiAnswer" - ], - "type": "boolean" - }, { "path": [ "mappings" @@ -177,43 +125,5 @@ "class" ], "type": "string" - }, - { - "path": [ - "mappings", - "hideInAnswer" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": [ - "string", - "boolean" - ] - } - ] - }, - { - "path": [ - "mappings", - "ifnot" - ], - "type": [ - { - "$ref": "#/definitions/AndOrTagConfigJson" - }, - { - "type": "string" - } - ] - }, - { - "path": [ - "mappings", - "addExtraTags" - ], - "type": "array" } ] \ No newline at end of file diff --git a/scripts/fixSchemas.ts b/scripts/fixSchemas.ts index 5bf91fcb6..4817c4960 100644 --- a/scripts/fixSchemas.ts +++ b/scripts/fixSchemas.ts @@ -6,7 +6,7 @@ interface JsonSchema { description?: string, type?: string, properties?: any, - items?: JsonSchema | JsonSchema[], + items?: JsonSchema, anyOf: JsonSchema[], enum: JsonSchema[], "$ref": string @@ -15,7 +15,6 @@ interface JsonSchema { function WalkScheme( onEach: (schemePart: JsonSchema) => T, scheme: JsonSchema, - registerSchemePath = false, fullScheme: JsonSchema & { definitions?: any } = undefined, path: string[] = [], isHandlingReference = [] @@ -24,6 +23,7 @@ function WalkScheme( if (scheme === undefined) { return [] } + if (scheme["$ref"] !== undefined) { const ref = scheme["$ref"] const prefix = "#/definitions/" @@ -35,70 +35,48 @@ function WalkScheme( return; } const loadedScheme = fullScheme.definitions[definitionName] - return WalkScheme(onEach, loadedScheme, registerSchemePath, fullScheme, path, [...isHandlingReference, definitionName]); + return WalkScheme(onEach, loadedScheme, fullScheme, path, [...isHandlingReference, definitionName]); } fullScheme = fullScheme ?? scheme var t = onEach(scheme) if (t !== undefined) { results.push({ - path: [...path], + path, t }) } - - - function walk(v: JsonSchema, pathPart: string) { + + + function walk(v: JsonSchema) { if (v === undefined) { return } - if (registerSchemePath) { - path.push("" + pathPart) - } - results.push(...WalkScheme(onEach, v, registerSchemePath, fullScheme, path, isHandlingReference)) - if (registerSchemePath) { - path.pop() - } + results.push(...WalkScheme(onEach, v, fullScheme, path, isHandlingReference)) } - function walkEach(scheme: JsonSchema[], pathPart: string) { + function walkEach(scheme: JsonSchema[]) { if (scheme === undefined) { return } - if (registerSchemePath) { - path.push("" + pathPart) - } - scheme.forEach((v, i) => walk(v, "" + i)) - if (registerSchemePath) { - path.pop() - } + + scheme.forEach(v => walk(v)) + } - { - walkEach(scheme.enum, "enum") - walkEach(scheme.anyOf, "anyOf") - if (scheme.items !== undefined) { - - if (scheme.items["forEach"] !== undefined) { - walkEach(scheme.items, "items") - } else { - walk(scheme.items, "items") - } + walkEach(scheme.enum) + walkEach(scheme.anyOf) + if (Array.isArray(scheme.items)) { + walkEach(scheme.items) + } else { + walk(scheme.items) } - if (registerSchemePath) { - path.push("properties") - } for (const key in scheme.properties) { const prop = scheme.properties[key] - path.push(key) - results.push(...WalkScheme(onEach, prop, registerSchemePath, fullScheme, path, isHandlingReference)) - path.pop() - } - if (registerSchemePath) { - path.pop() + results.push(...WalkScheme(onEach, prop, fullScheme, [...path, key], isHandlingReference)) } } @@ -114,14 +92,16 @@ function extractMeta(typename: string, path: string) { const typeHint = schemePart.description.split("\n") .find(line => line.trim().toLocaleLowerCase().startsWith("type:")) ?.substr("type:".length)?.trim() - const type = schemePart.type ?? schemePart.anyOf; + const type = schemePart.items?.anyOf ?? schemePart.type ?? schemePart.anyOf; return {typeHint, type} }, themeSchema) - writeFileSync("./assets/" + path + ".json", JSON.stringify(withTypes.map(({ - path, - t - }) => ({path, ...t})), null, " ")) + const paths = withTypes.map(({ + path, + t + }) => ({path, ...t})) + writeFileSync("./assets/" + path + ".json", JSON.stringify(paths, null, " ")) + console.log("Written meta to ./assets/" + path) } @@ -148,6 +128,7 @@ function main() { extractMeta("LayoutConfigJson", "layoutconfigmeta") extractMeta("TagRenderingConfigJson", "tagrenderingconfigmeta") + extractMeta("QuestionableTagRenderingConfigJson", "questionabletagrenderingconfigmeta") } diff --git a/test/LegacyThemeLoader.spec.ts b/test/LegacyThemeLoader.spec.ts index 8b1972120..5abd0ba49 100644 --- a/test/LegacyThemeLoader.spec.ts +++ b/test/LegacyThemeLoader.spec.ts @@ -449,7 +449,7 @@ export default class LegacyThemeLoaderSpec extends T { Assert.equal("https://raw.githubusercontent.com/seppesantens/MapComplete-Themes/main/VerkeerdeBordenDatabank/Something.svg", fixedMapping) }], - ["Images in 'thens' are detected", () => { + ["Images in simple mappings are detected", () => { const r = new DetectMappingsWithImages().convert({ "mappings": [ { @@ -470,7 +470,7 @@ export default class LegacyThemeLoaderSpec extends T { T.isTrue(errors.length > 0, "No images found"); T.isTrue(errors.some(msg => msg.indexOf("./assets/layers/bike_parking/staple.svg") >= 0), "staple.svg not mentioned"); }], - ["Images in 'thens' icons are detected", () => { + ["Images in 'thens' are detected in QuestionableTagRenderings", () => { const r = new ExtractImages(true, new Map()).convert({ "layers": [ { @@ -504,6 +504,26 @@ export default class LegacyThemeLoaderSpec extends T { T.isTrue(images.length > 0, "No images found"); T.isTrue(images.findIndex(img => img =="./assets/layers/bike_parking/staple.svg") >= 0, "staple.svg not mentioned"); T.isTrue(images.findIndex(img => img == "./assets/layers/bike_parking/bollard.svg") >= 0, "bollard.svg not mentioned"); + }], + ["Rotation and colours is not detected as image", () => { + const r = new ExtractImages(true, new Map()).convert({ + "layers": [ + { + mapRendering: [ + { + "location":["point","centroid"], + "icon": "pin:black", + rotation: 180, + iconSize: "40,40,center" + } + ] + } + ] + }, "test"); + const images = r.result + T.isTrue(images.length > 0, "No images found"); + T.isTrue(images.length < 2, "To much images found: "+images.join(", ")); + T.isTrue(images[0] === "pin", "pin not mentioned"); }] ] );