forked from MapComplete/MapComplete
Huge refactorings of JSON-parsing and Tagsfilter, other cleanups, warning cleanups and lots of small subtle bugfixes
This commit is contained in:
parent
9a5b35b9f3
commit
a57b7d93fa
113 changed files with 1565 additions and 2594 deletions
|
@ -1,6 +1,5 @@
|
|||
import {LayerDefinition} from "./LayerDefinition";
|
||||
import {Layout} from "./Layout";
|
||||
import {All} from "./Layouts/All";
|
||||
import {Groen} from "./Layouts/Groen";
|
||||
import Cyclofix from "./Layouts/Cyclofix";
|
||||
import {StreetWidth} from "./Layouts/StreetWidth";
|
||||
|
@ -10,12 +9,14 @@ import {Smoothness} from "./Layouts/Smoothness";
|
|||
import {MetaMap} from "./Layouts/MetaMap";
|
||||
import {Natuurpunt} from "./Layouts/Natuurpunt";
|
||||
import {GhostBikes} from "./Layouts/GhostBikes";
|
||||
import {CustomLayoutFromJSON} from "./JSON/CustomLayoutFromJSON";
|
||||
import {FromJSON} from "./JSON/FromJSON";
|
||||
import * as bookcases from "../assets/themes/bookcases/Bookcases.json";
|
||||
import * as aed from "../assets/themes/aed/aed.json";
|
||||
import * as toilets from "../assets/themes/toilets/toilets.json";
|
||||
import * as artworks from "../assets/themes/artwork/artwork.json";
|
||||
import * as cyclestreets from "../assets/themes/cyclestreets/cyclestreets.json";
|
||||
|
||||
|
||||
import {PersonalLayout} from "../Logic/PersonalLayout";
|
||||
|
||||
export class AllKnownLayouts {
|
||||
|
@ -28,11 +29,11 @@ export class AllKnownLayouts {
|
|||
new GRB(),
|
||||
new Cyclofix(),
|
||||
new GhostBikes(),
|
||||
CustomLayoutFromJSON.LayoutFromJSON(bookcases),
|
||||
CustomLayoutFromJSON.LayoutFromJSON(aed),
|
||||
CustomLayoutFromJSON.LayoutFromJSON(toilets),
|
||||
CustomLayoutFromJSON.LayoutFromJSON(artworks),
|
||||
CustomLayoutFromJSON.LayoutFromJSON(cyclestreets),
|
||||
FromJSON.LayoutFromJSON(bookcases),
|
||||
// FromJSON.LayoutFromJSON(aed),
|
||||
// FromJSON.LayoutFromJSON(toilets),
|
||||
// FromJSON.LayoutFromJSON(artworks),
|
||||
// FromJSON.LayoutFromJSON(cyclestreets),
|
||||
|
||||
new MetaMap(),
|
||||
new StreetWidth(),
|
||||
|
@ -48,26 +49,22 @@ export class AllKnownLayouts {
|
|||
private static AllLayouts(): Map<string, Layout> {
|
||||
|
||||
|
||||
const all = new All();
|
||||
this.allLayers = new Map<string, LayerDefinition>();
|
||||
for (const layout of this.layoutsList) {
|
||||
for (const layer of layout.layers) {
|
||||
const key = layer.id;
|
||||
if (this.allLayers[layer.id] !== undefined) {
|
||||
continue;
|
||||
}
|
||||
this.allLayers[layer.id] = layer;
|
||||
this.allLayers[layer.id.toLowerCase()] = layer;
|
||||
all.layers.push(layer);
|
||||
}
|
||||
}
|
||||
|
||||
const allSets: Map<string, Layout> = new Map();
|
||||
for (const layout of this.layoutsList) {
|
||||
allSets[layout.name] = layout;
|
||||
allSets[layout.name.toLowerCase()] = layout;
|
||||
allSets[layout.id] = layout;
|
||||
allSets[layout.id.toLowerCase()] = layout;
|
||||
}
|
||||
allSets[all.name] = all;
|
||||
return allSets;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,306 +0,0 @@
|
|||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
import {LayerDefinition, Preset} from "../LayerDefinition";
|
||||
import {Layout} from "../Layout";
|
||||
import Translation from "../../UI/i18n/Translation";
|
||||
import Combine from "../../UI/Base/Combine";
|
||||
import {And, Tag} from "../../Logic/TagsFilter";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {UIEventSource} from "../../Logic/UIEventSource";
|
||||
import {TagDependantUIElementConstructor} from "../UIElementConstructor";
|
||||
import {Map} from "../Layers/Map";
|
||||
import {UIElement} from "../../UI/UIElement";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
|
||||
|
||||
export interface TagRenderingConfigJson {
|
||||
// If this key is present, then...
|
||||
key?: string,
|
||||
// Use this string to render
|
||||
render?: string | any,
|
||||
// One of string, int, nat, float, pfloat, email, phone. Default: string
|
||||
type?: string,
|
||||
// If it is not known (and no mapping below matches), this question is asked; a textfield is inserted in the rendering above
|
||||
question?: string | any,
|
||||
// If a value is added with the textfield, this extra tag is addded. Optional field
|
||||
addExtraTags?: string | { k: string, v: string }[];
|
||||
// Extra tags: rendering is only shown/asked if these tags are present
|
||||
condition?: string;
|
||||
// Alternatively, these tags are shown if they match - even if the key above is not there
|
||||
// If unknown, these become a radio button
|
||||
mappings?:
|
||||
{
|
||||
if: string,
|
||||
then: string | any
|
||||
}[]
|
||||
}
|
||||
|
||||
export interface LayerConfigJson {
|
||||
name: string;
|
||||
title: string | any | TagRenderingConfigJson;
|
||||
description: string | any;
|
||||
minzoom: number | string,
|
||||
icon?: TagRenderingConfigJson;
|
||||
color?: TagRenderingConfigJson;
|
||||
width?: TagRenderingConfigJson;
|
||||
overpassTags: string | { k: string, v: string }[];
|
||||
wayHandling?: number,
|
||||
widenFactor?: number,
|
||||
presets: {
|
||||
tags: string,
|
||||
title: string | any,
|
||||
description?: string | any,
|
||||
icon?: string
|
||||
}[],
|
||||
tagRenderings: TagRenderingConfigJson []
|
||||
}
|
||||
|
||||
export interface LayoutConfigJson {
|
||||
widenFactor?: number;
|
||||
name: string;
|
||||
title: string | any;
|
||||
description: string | any;
|
||||
maintainer: string;
|
||||
language: string | string[];
|
||||
layers: LayerConfigJson[],
|
||||
startZoom: string | number;
|
||||
startLat: string | number;
|
||||
startLon: string | number;
|
||||
/**
|
||||
* Either a URL or a base64 encoded value (which should include 'data:image/svg+xml;base64,'
|
||||
*/
|
||||
icon: string;
|
||||
}
|
||||
|
||||
export class CustomLayoutFromJSON {
|
||||
|
||||
|
||||
public static FromQueryParam(layoutFromBase64: string): Layout {
|
||||
return CustomLayoutFromJSON.LayoutFromJSON(JSON.parse(atob(layoutFromBase64)));
|
||||
}
|
||||
|
||||
public static TagRenderingFromJson(json: TagRenderingConfigJson): TagDependantUIElementConstructor {
|
||||
|
||||
if(json === undefined){
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (typeof (json) === "string") {
|
||||
return new FixedText(json);
|
||||
}
|
||||
|
||||
let freeform = undefined;
|
||||
if (json.render !== undefined) {
|
||||
const type = json.type ?? "text";
|
||||
let renderTemplate = CustomLayoutFromJSON.MaybeTranslation(json.render);;
|
||||
const template = renderTemplate.replace("{" + json.key + "}", "$" + type + "$");
|
||||
if(type === "url"){
|
||||
renderTemplate = json.render.replace("{" + json.key + "}",
|
||||
`<a href='{${json.key}}' target='_blank'>{${json.key}}</a>`
|
||||
);
|
||||
}
|
||||
|
||||
freeform = {
|
||||
key: json.key,
|
||||
template: template,
|
||||
renderTemplate: renderTemplate,
|
||||
extraTags: CustomLayoutFromJSON.TagsFromJson(json.addExtraTags),
|
||||
}
|
||||
if (freeform.key === "*") {
|
||||
freeform.key = "id"; // Id is always there -> always take the rendering. Used for 'icon' and 'stroke'
|
||||
}
|
||||
}
|
||||
|
||||
let mappings = undefined;
|
||||
if (json.mappings !== undefined) {
|
||||
mappings = [];
|
||||
for (const mapping of json.mappings) {
|
||||
mappings.push({
|
||||
k: new And(CustomLayoutFromJSON.TagsFromJson(mapping.if)),
|
||||
txt: CustomLayoutFromJSON.MaybeTranslation(mapping.then)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const rendering = new TagRenderingOptions({
|
||||
question: CustomLayoutFromJSON.MaybeTranslation(json.question),
|
||||
freeform: freeform,
|
||||
mappings: mappings
|
||||
});
|
||||
|
||||
if (json.condition) {
|
||||
const conditionTags: Tag[] = CustomLayoutFromJSON.TagsFromJson(json.condition);
|
||||
return rendering.OnlyShowIf(new And(conditionTags));
|
||||
}
|
||||
return rendering;
|
||||
}
|
||||
|
||||
private static PresetFromJson(layout: any, preset: any): Preset {
|
||||
const t = CustomLayoutFromJSON.MaybeTranslation;
|
||||
const tags = CustomLayoutFromJSON.TagsFromJson;
|
||||
return {
|
||||
icon: preset.icon ?? CustomLayoutFromJSON.TagRenderingFromJson(layout.icon),
|
||||
tags: tags(preset.tags) ?? tags(layout.overpassTags),
|
||||
title: t(preset.title) ?? t(layout.title),
|
||||
description: t(preset.description) ?? t(layout.description)
|
||||
}
|
||||
}
|
||||
|
||||
private static StyleFromJson(layout: LayerConfigJson): ((tags: any) => {
|
||||
color: string,
|
||||
weight?: number,
|
||||
icon: {
|
||||
iconUrl: string,
|
||||
iconSize: number[],
|
||||
},
|
||||
}) {
|
||||
const iconRendering: TagDependantUIElementConstructor = CustomLayoutFromJSON.TagRenderingFromJson(layout.icon);
|
||||
const colourRendering = CustomLayoutFromJSON.TagRenderingFromJson(layout.color);
|
||||
let thickness = CustomLayoutFromJSON.TagRenderingFromJson(layout.width);
|
||||
|
||||
|
||||
return (tags) => {
|
||||
const iconUrl = iconRendering.GetContent(tags);
|
||||
const stroke = colourRendering.GetContent(tags) ?? "#00f";
|
||||
let weight = parseInt(thickness?.GetContent(tags)) ?? 10;
|
||||
if(isNaN(weight)){
|
||||
weight = 10;
|
||||
}
|
||||
return {
|
||||
color: stroke,
|
||||
weight: weight,
|
||||
icon: {
|
||||
iconUrl: iconUrl,
|
||||
iconSize: [40, 40],
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static TagFromJson(json: string | { k: string, v: string }): Tag {
|
||||
if (json === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof (json) !== "string") {
|
||||
return new Tag(json.k.trim(), json.v.trim())
|
||||
}
|
||||
|
||||
let kv: string[] = undefined;
|
||||
let invert = false;
|
||||
let regex = false;
|
||||
if (json.indexOf("!=") >= 0) {
|
||||
kv = json.split("!=");
|
||||
invert = true;
|
||||
} else if (json.indexOf("~=") >= 0) {
|
||||
kv = json.split("~=");
|
||||
regex = true;
|
||||
} else {
|
||||
kv = json.split("=");
|
||||
}
|
||||
|
||||
if (kv.length !== 2) {
|
||||
return undefined;
|
||||
}
|
||||
if (kv[0].trim() === "") {
|
||||
return undefined;
|
||||
}
|
||||
let v = kv[1].trim();
|
||||
if(v.startsWith("/") && v.endsWith("/")){
|
||||
v = v.substr(1, v.length - 2);
|
||||
regex = true;
|
||||
}
|
||||
return new Tag(kv[0].trim(), regex ? new RegExp(v): v, invert);
|
||||
}
|
||||
|
||||
public static TagsFromJson(json: string | { k: string, v: string }[]): Tag[] {
|
||||
if (json === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (json === "") {
|
||||
return [];
|
||||
}
|
||||
let tags = [];
|
||||
if (typeof (json) === "string") {
|
||||
tags = json.split("&").map(CustomLayoutFromJSON.TagFromJson);
|
||||
} else {
|
||||
tags = json.map(x => {CustomLayoutFromJSON.TagFromJson(x)});
|
||||
}
|
||||
for (const tag of tags) {
|
||||
if (tag === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
private static LayerFromJson(json: LayerConfigJson): LayerDefinition {
|
||||
const t = CustomLayoutFromJSON.MaybeTranslation;
|
||||
const tr = CustomLayoutFromJSON.TagRenderingFromJson;
|
||||
const tags = CustomLayoutFromJSON.TagsFromJson(json.overpassTags);
|
||||
// We run the icon rendering with the bare minimum of tags (the overpass tags) to get the actual icon
|
||||
const icon = CustomLayoutFromJSON.TagRenderingFromJson(json.icon).GetContent({id:"node/-1"});
|
||||
|
||||
// @ts-ignore
|
||||
const id = json.name?.replace(/[^a-zA-Z0-9_-]/g,'') ?? json.id;
|
||||
return new LayerDefinition(
|
||||
id,
|
||||
{
|
||||
description: t(json.description),
|
||||
name: Translations.WT(t(json.name)),
|
||||
icon: icon,
|
||||
minzoom: parseInt(""+json.minzoom),
|
||||
title: tr(json.title),
|
||||
presets: json.presets.map((preset) => {
|
||||
return CustomLayoutFromJSON.PresetFromJson(json, preset)
|
||||
}),
|
||||
elementsToShow:
|
||||
[new ImageCarouselWithUploadConstructor()].concat(json.tagRenderings.map(tr)),
|
||||
overpassFilter: new And(tags),
|
||||
wayHandling: parseInt(""+json.wayHandling) ?? LayerDefinition.WAYHANDLING_CENTER_AND_WAY,
|
||||
maxAllowedOverlapPercentage: 0,
|
||||
style: CustomLayoutFromJSON.StyleFromJson(json)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
private static MaybeTranslation(json: any): Translation | string {
|
||||
if (json === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof (json) === "string") {
|
||||
return json;
|
||||
}
|
||||
return new Translation(json);
|
||||
}
|
||||
|
||||
public static LayoutFromJSON(json: LayoutConfigJson) {
|
||||
const t = CustomLayoutFromJSON.MaybeTranslation;
|
||||
let languages : string[] ;
|
||||
if(typeof (json.language) === "string"){
|
||||
languages = [json.language];
|
||||
}else{
|
||||
languages = json.language
|
||||
}
|
||||
const layout = new Layout(json.name,
|
||||
languages,
|
||||
t(json.title),
|
||||
json.layers.map(CustomLayoutFromJSON.LayerFromJson),
|
||||
parseInt(""+json.startZoom),
|
||||
parseFloat(""+json.startLat),
|
||||
parseFloat(""+json.startLon),
|
||||
new Combine(['<h3>', t(json.title), '</h3><br/>', t(json.description)])
|
||||
);
|
||||
layout.icon = json.icon;
|
||||
layout.maintainer = json.maintainer;
|
||||
layout.widenFactor = parseFloat(""+json.widenFactor) ?? 0.03;
|
||||
if(isNaN(layout.widenFactor)){
|
||||
layout.widenFactor = 0.03;
|
||||
}
|
||||
if (layout.widenFactor > 0.1) {
|
||||
layout.widenFactor = 0.1;
|
||||
}
|
||||
return layout;
|
||||
}
|
||||
|
||||
}
|
259
Customizations/JSON/FromJSON.ts
Normal file
259
Customizations/JSON/FromJSON.ts
Normal file
|
@ -0,0 +1,259 @@
|
|||
import {Layout} from "../Layout";
|
||||
import {LayoutConfigJson} from "./LayoutConfigJson";
|
||||
import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||
import {And, RegexTag, Tag, TagsFilter} from "../../Logic/Tags";
|
||||
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
import Translation from "../../UI/i18n/Translation";
|
||||
import {LayerConfigJson} from "./LayerConfigJson";
|
||||
import {LayerDefinition, Preset} from "../LayerDefinition";
|
||||
import {TagDependantUIElementConstructor} from "../UIElementConstructor";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import Combine from "../../UI/Base/Combine";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {ImageCarouselConstructor} from "../../UI/Image/ImageCarousel";
|
||||
|
||||
|
||||
export class FromJSON {
|
||||
|
||||
|
||||
public static FromBase64(layoutFromBase64: string): Layout {
|
||||
return FromJSON.LayoutFromJSON(JSON.parse(atob(layoutFromBase64)));
|
||||
}
|
||||
|
||||
public static LayoutFromJSON(json: LayoutConfigJson): Layout {
|
||||
console.log("Parsing ", json.id)
|
||||
const tr = FromJSON.Translation;
|
||||
|
||||
const layers = json.layers.map(FromJSON.Layer);
|
||||
const roaming: TagDependantUIElementConstructor[] = json.roamingRenderings?.map(FromJSON.TagRendering) ?? [];
|
||||
for (const layer of layers) {
|
||||
layer.elementsToShow.push(...roaming);
|
||||
}
|
||||
|
||||
const layout = new Layout(
|
||||
json.id,
|
||||
typeof (json.language) === "string" ? [json.language] : json.language,
|
||||
tr(json.title),
|
||||
layers,
|
||||
json.startZoom,
|
||||
json.startLat,
|
||||
json.startLon,
|
||||
new Combine(["<h3>", tr(json.title), "</h3>", tr(json.description)]),
|
||||
);
|
||||
|
||||
layout.widenFactor = json.widenFactor ?? 0.07;
|
||||
layout.icon = json.icon;
|
||||
layout.maintainer = json.maintainer;
|
||||
layout.version = json.version;
|
||||
layout.socialImage = json.socialImage;
|
||||
layout.changesetMessage = json.changesetmessage;
|
||||
return layout;
|
||||
}
|
||||
|
||||
public static Translation(json: string | any): string | Translation {
|
||||
if (json === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof (json) === "string") {
|
||||
return json;
|
||||
}
|
||||
const tr = {};
|
||||
for (let key in json) {
|
||||
tr[key] = json[key]; // I'm doing this wrong, I know
|
||||
}
|
||||
return new Translation(tr);
|
||||
}
|
||||
|
||||
public static TagRendering(json: TagRenderingConfigJson | string): TagDependantUIElementConstructor {
|
||||
return FromJSON.TagRenderingWithDefault(json, "", undefined);
|
||||
}
|
||||
|
||||
public static TagRenderingWithDefault(json: TagRenderingConfigJson | string, propertyName, defaultValue: string): TagDependantUIElementConstructor {
|
||||
if (json === undefined) {
|
||||
if(defaultValue !== undefined){
|
||||
console.warn(`Using default value ${defaultValue} for ${propertyName}`)
|
||||
return FromJSON.TagRendering(defaultValue);
|
||||
}
|
||||
throw `Tagrendering ${propertyName} is undefined...`
|
||||
}
|
||||
|
||||
if (typeof json === "string") {
|
||||
|
||||
switch (json) {
|
||||
case "picture": {
|
||||
return new ImageCarouselWithUploadConstructor()
|
||||
}
|
||||
case "pictures": {
|
||||
return new ImageCarouselWithUploadConstructor()
|
||||
}
|
||||
case "image": {
|
||||
return new ImageCarouselWithUploadConstructor()
|
||||
}
|
||||
case "images": {
|
||||
return new ImageCarouselWithUploadConstructor()
|
||||
}
|
||||
case "picturesNoUpload": {
|
||||
return new ImageCarouselConstructor()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new TagRenderingOptions({
|
||||
freeform: {
|
||||
key: "id",
|
||||
renderTemplate: json,
|
||||
template: "$$$"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let template = FromJSON.Translation(json.render);
|
||||
|
||||
let freeform = undefined;
|
||||
if (json.freeform) {
|
||||
|
||||
if(json.render === undefined){
|
||||
console.error("Freeform is defined, but render is not. This is not allowed.", json)
|
||||
throw "Freeform is defined, but render is not. This is not allowed."
|
||||
}
|
||||
|
||||
freeform = {
|
||||
template: `$${json.freeform.type ?? "string"}$`,
|
||||
renderTemplate: template,
|
||||
key: json.freeform.key
|
||||
};
|
||||
if (json.freeform.addExtraTags) {
|
||||
freeform["extraTags"] = FromJSON.Tag(json.freeform.addExtraTags);
|
||||
}
|
||||
} else if (json.render) {
|
||||
freeform = {
|
||||
template: `$string$`,
|
||||
renderTemplate: template,
|
||||
key: "id"
|
||||
}
|
||||
}
|
||||
|
||||
const mappings = json.mappings?.map(mapping => (
|
||||
{
|
||||
k: FromJSON.Tag(mapping.if),
|
||||
txt: FromJSON.Translation(mapping.then),
|
||||
hideInAnswer: mapping.hideInAnswer
|
||||
})
|
||||
);
|
||||
|
||||
return new TagRenderingOptions({
|
||||
question: FromJSON.Translation(json.question),
|
||||
freeform: freeform,
|
||||
mappings: mappings
|
||||
});
|
||||
}
|
||||
|
||||
public static SimpleTag(json: string): Tag {
|
||||
const tag = json.split("=");
|
||||
return new Tag(tag[0], tag[1]);
|
||||
}
|
||||
|
||||
public static Tag(json: AndOrTagConfigJson | string): TagsFilter {
|
||||
if (typeof (json) == "string") {
|
||||
const tag = json as string;
|
||||
if (tag.indexOf("!~") >= 0) {
|
||||
const split = tag.split("!~");
|
||||
if(split[1] == "*"){
|
||||
split[1] = ".*"
|
||||
}
|
||||
return new RegexTag(
|
||||
new RegExp(split[0]),
|
||||
new RegExp(split[1]),
|
||||
true
|
||||
);
|
||||
}
|
||||
if (tag.indexOf("!=") >= 0) {
|
||||
const split = tag.split("!=");
|
||||
return new RegexTag(
|
||||
new RegExp(split[0]),
|
||||
new RegExp(split[1]),
|
||||
true
|
||||
);
|
||||
}
|
||||
if (tag.indexOf("~") >= 0) {
|
||||
const split = tag.split("~");
|
||||
if(split[1] == "*"){
|
||||
split[1] = ".*"
|
||||
}
|
||||
return new RegexTag(
|
||||
new RegExp("^"+split[0]+"$"),
|
||||
new RegExp("^"+split[1]+"$")
|
||||
);
|
||||
}
|
||||
const split = tag.split("=");
|
||||
return new Tag(split[0], split[1])
|
||||
}
|
||||
if (json.and !== undefined) {
|
||||
return new And(json.and.map(FromJSON.Tag));
|
||||
}
|
||||
if (json.or !== undefined) {
|
||||
return new And(json.or.map(FromJSON.Tag));
|
||||
}
|
||||
}
|
||||
|
||||
private static Title(json: string | Map<string, string> | TagRenderingConfigJson): TagDependantUIElementConstructor {
|
||||
if ((json as TagRenderingConfigJson).render !== undefined) {
|
||||
return FromJSON.TagRendering((json as TagRenderingConfigJson));
|
||||
} else if (typeof (json) === "string") {
|
||||
return new FixedText(Translations.WT(json));
|
||||
} else {
|
||||
return new FixedText(FromJSON.Translation(json as Map<string, string>));
|
||||
}
|
||||
}
|
||||
|
||||
public static Layer(json: LayerConfigJson): LayerDefinition {
|
||||
console.log("Parsing ",json.name);
|
||||
const tr = FromJSON.Translation;
|
||||
const overpassTags = FromJSON.Tag(json.overpassTags);
|
||||
const icon = FromJSON.TagRenderingWithDefault(json.icon, "layericon", "./assets/bug.svg");
|
||||
const color = FromJSON.TagRenderingWithDefault(json.color, "layercolor", "#0000ff");
|
||||
const width = FromJSON.TagRenderingWithDefault(json.width, "layerwidth", "10");
|
||||
const renderTags = {"id": "node/-1"}
|
||||
const presets: Preset[] = json?.presets?.map(preset => {
|
||||
return ({
|
||||
title: tr(preset.title),
|
||||
description: tr(preset.description),
|
||||
tags: preset.tags.map(FromJSON.SimpleTag)
|
||||
});
|
||||
}) ?? [];
|
||||
|
||||
function style(tags) {
|
||||
return {
|
||||
color: color.GetContent(tags).txt,
|
||||
weight: width.GetContent(tags).txt,
|
||||
icon: {
|
||||
iconUrl: icon.GetContent(tags).txt
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const layer = new LayerDefinition(
|
||||
json.id,
|
||||
{
|
||||
name: tr(json.name),
|
||||
description: tr(json.description),
|
||||
icon: icon.GetContent(renderTags).txt,
|
||||
overpassFilter: overpassTags,
|
||||
|
||||
title: FromJSON.Title(json.title),
|
||||
minzoom: json.minzoom,
|
||||
presets: presets,
|
||||
elementsToShow: json.tagRenderings?.map(FromJSON.TagRendering) ?? [],
|
||||
style: style,
|
||||
wayHandling: json.wayHandling
|
||||
|
||||
}
|
||||
);
|
||||
return layer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
79
Customizations/JSON/LayerConfigJson.ts
Normal file
79
Customizations/JSON/LayerConfigJson.ts
Normal file
|
@ -0,0 +1,79 @@
|
|||
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
||||
import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||
|
||||
/**
|
||||
* Configuration for a single layer
|
||||
*/
|
||||
export interface LayerConfigJson {
|
||||
/**
|
||||
* The id of this layer.
|
||||
* This should be a simple, lowercase, human readable string that is used to identify the layer.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The name of this layer
|
||||
* Used in the layer control panel and the 'Personal theme'
|
||||
*/
|
||||
name: string | any
|
||||
|
||||
/**
|
||||
* A description for this layer.
|
||||
* Shown in the layer selections and in the personal theme
|
||||
*/
|
||||
description?: string | any;
|
||||
|
||||
|
||||
/**
|
||||
* The tags to load from overpass. Either a simple 'key=value'-string, otherwise an advanced configuration
|
||||
*/
|
||||
overpassTags: AndOrTagConfigJson | string;
|
||||
|
||||
/**
|
||||
* The zoomlevel at which point the data is shown and loaded.
|
||||
*/
|
||||
minzoom: number;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The title shown in a popup for elements of this layer
|
||||
*/
|
||||
title: string | any | TagRenderingConfigJson;
|
||||
|
||||
/**
|
||||
* The icon for an element.
|
||||
* Note that this also doubles as the icon for this layer (rendered with the overpass-tags) ánd the icon in the presets.
|
||||
*/
|
||||
icon?: string | TagRenderingConfigJson;
|
||||
/**
|
||||
* The color for way-elements
|
||||
*/
|
||||
color?: string | TagRenderingConfigJson;
|
||||
/**
|
||||
* The stroke-width for way-elements
|
||||
*/
|
||||
width?: string | TagRenderingConfigJson;
|
||||
|
||||
/**
|
||||
* Wayhandling: should a way/area be displayed as:
|
||||
* 0) The way itself
|
||||
* 1) The centerpoint and the way
|
||||
* 2) Only the centerpoint?
|
||||
*/
|
||||
wayHandling?: number;
|
||||
|
||||
/**
|
||||
* Presets for this layer
|
||||
*/
|
||||
presets?: {
|
||||
tags: string[],
|
||||
title: string | any,
|
||||
description?: string | any,
|
||||
}[],
|
||||
|
||||
/**
|
||||
* All the tag renderings.
|
||||
*/
|
||||
tagRenderings?: (string | TagRenderingConfigJson) []
|
||||
}
|
98
Customizations/JSON/LayoutConfigJson.ts
Normal file
98
Customizations/JSON/LayoutConfigJson.ts
Normal file
|
@ -0,0 +1,98 @@
|
|||
import {LayerConfigJson} from "./LayerConfigJson";
|
||||
import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
|
||||
|
||||
/**
|
||||
* Defines what a JSON-segment defining a layout should look like.
|
||||
*
|
||||
* General remark: a type (string | any) indicates either a fixed or a translatable string
|
||||
*/
|
||||
export interface LayoutConfigJson {
|
||||
/**
|
||||
* The id of this layout.
|
||||
* This should be a simple, lowercase string which is used to create the html-page, e.g.
|
||||
* 'cyclestreets' which become 'cyclestreets.html'
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* Who does maintian this preset?
|
||||
*/
|
||||
maintainer: string;
|
||||
/**
|
||||
* Extra piece of text that can be added to the changeset
|
||||
*/
|
||||
changesetmessage?: string;
|
||||
/**
|
||||
* A version number, either semantically or by date.
|
||||
* Should be sortable, where the higher value is the later version
|
||||
*/
|
||||
version: string;
|
||||
/**
|
||||
* The supported language(s).
|
||||
* This should be a two-letter, lowercase code which identifies the language, e.g. "en", "nl", ...
|
||||
* If the theme supports multiple languages, use a list: `["en","nl","fr"]` to allow the user to pick any of them
|
||||
*/
|
||||
language: string | string[];
|
||||
|
||||
/**
|
||||
* The title, as shown in the welcome message and the more-screen
|
||||
*/
|
||||
title: string | any;
|
||||
/**
|
||||
* The description, as shown in the welcome message and the more-screen
|
||||
*/
|
||||
description: string | any;
|
||||
|
||||
|
||||
/**
|
||||
* The icon representing this theme.
|
||||
* Used as logo in the more-screen and (for official themes) as favicon, webmanifest logo, ...
|
||||
* Either a URL or a base64 encoded value (which should include 'data:image/svg+xml;base64)
|
||||
*/
|
||||
icon: string;
|
||||
|
||||
/**
|
||||
* Link to a 'social image' which is included as og:image-tag on official themes.
|
||||
* Usefull to share the theme on social media
|
||||
*/
|
||||
socialImage?: string;
|
||||
|
||||
/**
|
||||
* Default location and zoom to start.
|
||||
* Note that this is barely used. Once the user has visited mapcomplete at least once, the previous location of the user will be used
|
||||
*/
|
||||
startZoom: number;
|
||||
startLat: number;
|
||||
startLon: number;
|
||||
|
||||
/**
|
||||
* When a query is run, the data within bounds of the visible map is loaded.
|
||||
* However, users tend to pan and zoom a lot. It is pretty annoying if every single pan means a reloading of the data.
|
||||
* For this, the bounds are widened in order to make a small pan still within bounds of the loaded data.
|
||||
*
|
||||
* IF widenfactor is 0, this feature is disabled. A recommended value is between 0.5 and 0.01 (the latter for very dense queries)
|
||||
*/
|
||||
widenFactor?: number;
|
||||
|
||||
/**
|
||||
* A tagrendering depicts how to show some tags or how to show a question for it.
|
||||
*
|
||||
* These tagrenderings are applied to _all_ the loaded layers and are a way to reuse tagrenderings.
|
||||
* Note that if multiple themes are loaded (e.g. via the personal theme)
|
||||
* that these roamingRenderings are applied to the layers of the OTHER themes too!
|
||||
*
|
||||
* In order to prevent them to do too much damage, all the overpass-tags of the layers are taken and combined as OR.
|
||||
* These tag renderings will only show up if the object matches this filter.
|
||||
*/
|
||||
roamingRenderings?: TagRenderingConfigJson[],
|
||||
|
||||
/**
|
||||
* The layers to display
|
||||
*/
|
||||
layers: LayerConfigJson[],
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
14
Customizations/JSON/TagConfig.ts
Normal file
14
Customizations/JSON/TagConfig.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
/**
|
||||
* Read a tagconfig and converts it into a TagsFilter value
|
||||
*/
|
||||
import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||
|
||||
export default class TagConfig {
|
||||
|
||||
public static fromJson(json: any): TagConfig {
|
||||
const config: AndOrTagConfigJson = json;
|
||||
return config;
|
||||
}
|
||||
|
||||
}
|
||||
|
8
Customizations/JSON/TagConfigJson.ts
Normal file
8
Customizations/JSON/TagConfigJson.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
export interface AndOrTagConfigJson {
|
||||
|
||||
and?: (string | AndOrTagConfigJson)[]
|
||||
or?: (string | AndOrTagConfigJson)[]
|
||||
|
||||
|
||||
}
|
51
Customizations/JSON/TagRenderingConfigJson.ts
Normal file
51
Customizations/JSON/TagRenderingConfigJson.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import {AndOrTagConfigJson} from "./TagConfigJson";
|
||||
|
||||
export interface TagRenderingConfigJson {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
freeform?: {
|
||||
/**
|
||||
* If this key is present, then 'render' is used to display the value.
|
||||
* If this is undefined, the rendering is _always_ shown
|
||||
*/
|
||||
key: string,
|
||||
/**
|
||||
* The type of the text-field, e.g. 'string', 'nat', 'float', 'date',...
|
||||
*/
|
||||
type?: string,
|
||||
/**
|
||||
* If a value is added with the textfield, these extra tag is addded.
|
||||
* Usefull to add a 'fixme=freeform textfield used - to be checked'
|
||||
**/
|
||||
addExtraTags?: AndOrTagConfigJson | string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows fixed-tag inputs, shown either as radiobuttons or as checkboxes
|
||||
*/
|
||||
mappings?: {
|
||||
if: AndOrTagConfigJson | string,
|
||||
then: string | any
|
||||
hideInAnswer?: boolean
|
||||
}[]
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import {Tag, TagsFilter} from "../Logic/TagsFilter";
|
||||
import {Tag, TagsFilter} from "../Logic/Tags";
|
||||
import {UIElement} from "../UI/UIElement";
|
||||
import {TagDependantUIElementConstructor} from "./UIElementConstructor";
|
||||
import {TagRenderingOptions} from "./TagRenderingOptions";
|
||||
import Translation from "../UI/i18n/Translation";
|
||||
import {LayerConfigJson, TagRenderingConfigJson} from "./JSON/CustomLayoutFromJSON";
|
||||
|
||||
export interface Preset {
|
||||
tags: Tag[],
|
||||
|
@ -75,9 +74,8 @@ export class LayerDefinition {
|
|||
style: (tags: any) => {
|
||||
color: string,
|
||||
weight?: number,
|
||||
icon: {
|
||||
iconUrl: string,
|
||||
iconSize: number[],
|
||||
icon: {
|
||||
iconUrl: string, iconSize?: number[], popupAnchor?: number[], iconAnchor?: number[]
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import FixedText from "../Questions/FixedText";
|
|||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import CafeName from "../Questions/bike/CafeName";
|
||||
import { Or, And, Tag, anyValueExcept, Regex } from "../../Logic/TagsFilter";
|
||||
import { PhoneNumberQuestion } from "../Questions/PhoneNumberQuestion";
|
||||
import {And, Or, RegexTag, Tag} from "../../Logic/Tags";
|
||||
import {PhoneNumberQuestion} from "../Questions/PhoneNumberQuestion";
|
||||
import Website from "../Questions/Website";
|
||||
import CafeRepair from "../Questions/bike/CafeRepair";
|
||||
import CafeDiy from "../Questions/bike/CafeDiy";
|
||||
|
@ -20,10 +20,11 @@ export default class BikeCafes extends LayerDefinition {
|
|||
this.name = this.to.name
|
||||
this.icon = "./assets/bike/cafe.svg"
|
||||
this.overpassFilter = new And([
|
||||
new Tag("amenity", /^pub|bar|cafe$/),
|
||||
new RegexTag(/^amenity$/, /^pub|bar|cafe$/),
|
||||
new Or([
|
||||
new Tag(/^service:bicycle:/, "*"),
|
||||
new Tag("pub", "cycling")
|
||||
new RegexTag(/^service:bicycle:/, /.*/),
|
||||
new RegexTag(/^pub$/, /^cycling|bicycle$/),
|
||||
new RegexTag(/^theme$/, /^cycling|bicycle$/),
|
||||
])
|
||||
])
|
||||
|
||||
|
@ -40,7 +41,14 @@ export default class BikeCafes extends LayerDefinition {
|
|||
this.maxAllowedOverlapPercentage = 10;
|
||||
|
||||
this.minzoom = 13
|
||||
this.style = this.generateStyleFunction()
|
||||
this.style = () => ({
|
||||
color: "#00bb00",
|
||||
icon: {
|
||||
iconUrl: "./assets/bike/cafe.svg",
|
||||
iconSize: [50, 50],
|
||||
iconAnchor: [25, 50]
|
||||
}
|
||||
});
|
||||
this.title = new FixedText(this.to.title)
|
||||
this.elementsToShow = [
|
||||
new ImageCarouselWithUploadConstructor(),
|
||||
|
@ -54,18 +62,4 @@ export default class BikeCafes extends LayerDefinition {
|
|||
]
|
||||
this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY
|
||||
}
|
||||
|
||||
private generateStyleFunction() {
|
||||
const self = this
|
||||
return function (properties: any) {
|
||||
return {
|
||||
color: "#00bb00",
|
||||
icon: {
|
||||
iconUrl: "./assets/bike/cafe.svg",
|
||||
iconSize: [50, 50],
|
||||
iconAnchor: [25,50]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import { LayerDefinition } from "../LayerDefinition";
|
||||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import {And, Tag, Or, anyValueExcept} from "../../Logic/TagsFilter";
|
||||
import { ImageCarouselWithUploadConstructor } from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {And, RegexTag, Tag} from "../../Logic/Tags";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import ShopRetail from "../Questions/bike/ShopRetail";
|
||||
import ShopPump from "../Questions/bike/ShopPump";
|
||||
import ShopRental from "../Questions/bike/ShopRental";
|
||||
|
@ -9,7 +9,7 @@ import ShopRepair from "../Questions/bike/ShopRepair";
|
|||
import ShopDiy from "../Questions/bike/ShopDiy";
|
||||
import ShopName from "../Questions/bike/ShopName";
|
||||
import ShopSecondHand from "../Questions/bike/ShopSecondHand";
|
||||
import { PhoneNumberQuestion } from "../Questions/PhoneNumberQuestion";
|
||||
import {PhoneNumberQuestion} from "../Questions/PhoneNumberQuestion";
|
||||
import Website from "../Questions/Website";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
|
@ -24,8 +24,8 @@ export default class BikeOtherShops extends LayerDefinition {
|
|||
this.name = this.to.name
|
||||
this.icon = "./assets/bike/non_bike_repair_shop.svg"
|
||||
this.overpassFilter = new And([
|
||||
anyValueExcept("shop", "bicycle"),
|
||||
new Tag(/^service:bicycle:/, "*"),
|
||||
new RegexTag(/^shop$/, /^bicycle$/, true),
|
||||
new RegexTag(/^service:bicycle:/, /.*/),
|
||||
])
|
||||
this.presets = []
|
||||
this.maxAllowedOverlapPercentage = 10
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Or, Tag, TagsFilter} from "../../Logic/TagsFilter";
|
||||
import {OperatorTag} from "../Questions/OperatorTag";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import ParkingType from "../Questions/bike/ParkingType";
|
||||
import ParkingCapacity from "../Questions/bike/ParkingCapacity";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import ParkingOperator from "../Questions/bike/ParkingOperator";
|
||||
import ParkingAccessCargo from "../Questions/bike/ParkingAccessCargo";
|
||||
import ParkingCapacityCargo from "../Questions/bike/ParkingCapacityCargo";
|
||||
|
||||
|
@ -28,8 +26,17 @@ export default class BikeParkings extends LayerDefinition {
|
|||
|
||||
this.maxAllowedOverlapPercentage = 10;
|
||||
|
||||
this.minzoom = 13;
|
||||
this.style = this.generateStyleFunction();
|
||||
this.minzoom = 17;
|
||||
this.style = function () {
|
||||
return {
|
||||
color: "#00bb00",
|
||||
icon: {
|
||||
iconUrl: "./assets/bike/parking.svg",
|
||||
iconSize: [50, 50],
|
||||
iconAnchor: [25, 50]
|
||||
}
|
||||
};
|
||||
};
|
||||
this.title = new FixedText(Translations.t.cyclofix.parking.title)
|
||||
this.elementsToShow = [
|
||||
new ImageCarouselWithUploadConstructor(),
|
||||
|
@ -42,18 +49,4 @@ export default class BikeParkings extends LayerDefinition {
|
|||
this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY;
|
||||
|
||||
}
|
||||
|
||||
private generateStyleFunction() {
|
||||
const self = this;
|
||||
return function (properties: any) {
|
||||
return {
|
||||
color: "#00bb00",
|
||||
icon: {
|
||||
iconUrl: "./assets/bike/parking.svg",
|
||||
iconSize: [50, 50],
|
||||
iconAnchor: [25,50]
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { LayerDefinition } from "../LayerDefinition";
|
||||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import {And, Tag, Or} from "../../Logic/TagsFilter";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import { ImageCarouselWithUploadConstructor } from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {And, Tag} from "../../Logic/Tags";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import ShopRetail from "../Questions/bike/ShopRetail";
|
||||
import ShopPump from "../Questions/bike/ShopPump";
|
||||
import ShopRental from "../Questions/bike/ShopRental";
|
||||
|
@ -18,7 +17,6 @@ import {TagRenderingOptions} from "../TagRenderingOptions";
|
|||
|
||||
export default class BikeShops extends LayerDefinition {
|
||||
private readonly sellsBikes = new Tag("service:bicycle:retail", "yes")
|
||||
private readonly repairsBikes = new Tag("service:bicycle:repair", "yes")
|
||||
|
||||
constructor() {
|
||||
super("bikeshop");
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Tag, TagsFilter, Or, Not} from "../../Logic/TagsFilter";
|
||||
import {And, Or, Tag} from "../../Logic/Tags";
|
||||
import BikeStationChain from "../Questions/bike/StationChain";
|
||||
import BikeStationPumpTools from "../Questions/bike/StationPumpTools";
|
||||
import BikeStationStand from "../Questions/bike/StationStand";
|
||||
import PumpManual from "../Questions/bike/PumpManual";
|
||||
import BikeStationOperator from "../Questions/bike/StationOperator";
|
||||
import BikeStationBrand from "../Questions/bike/StationBrand";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import PumpManometer from "../Questions/bike/PumpManometer";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import PumpOperational from "../Questions/bike/PumpOperational";
|
||||
|
@ -19,7 +16,6 @@ export default class BikeStations extends LayerDefinition {
|
|||
private readonly repairStation = new Tag("amenity", "bicycle_repair_station");
|
||||
private readonly pump = new Tag("service:bicycle:pump", "yes");
|
||||
private readonly nopump = new Tag("service:bicycle:pump", "no");
|
||||
private readonly pumpOperationalAny = new Tag("service:bicycle:pump:operational_status", "yes");
|
||||
private readonly pumpOperationalOk = new Or([new Tag("service:bicycle:pump:operational_status", "yes"), new Tag("service:bicycle:pump:operational_status", "operational"), new Tag("service:bicycle:pump:operational_status", "ok"), new Tag("service:bicycle:pump:operational_status", "")]);
|
||||
private readonly tools = new Tag("service:bicycle:tools", "yes");
|
||||
private readonly notools = new Tag("service:bicycle:tools", "no");
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Or, Tag} from "../../Logic/TagsFilter";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {And, Or, Tag} from "../../Logic/Tags";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
|
@ -24,7 +23,7 @@ export class Birdhide extends LayerDefinition {
|
|||
tags: [Birdhide.birdhide]
|
||||
}
|
||||
],
|
||||
style(tags: any): { color: string; icon: any } {
|
||||
style(): { color: string; icon: any } {
|
||||
return {color: "", icon: undefined};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {Or, Tag} from "../../Logic/Tags";
|
||||
import {AccessTag} from "../Questions/AccessTag";
|
||||
import {OperatorTag} from "../Questions/OperatorTag";
|
||||
import {NameQuestion} from "../Questions/NameQuestion";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {And, Tag} from "../../Logic/TagsFilter";
|
||||
import {And, Tag} from "../../Logic/Tags";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
|
||||
export class ClimbingTree extends LayerDefinition {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {And, Or, Tag} from "../../Logic/Tags";
|
||||
import {OperatorTag} from "../Questions/OperatorTag";
|
||||
import * as L from "leaflet";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
|
@ -29,7 +28,7 @@ export class DrinkingWater extends LayerDefinition {
|
|||
this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY
|
||||
|
||||
this.minzoom = 13;
|
||||
this.style = this.generateStyleFunction();
|
||||
this.style = DrinkingWater.generateStyleFunction();
|
||||
this.title = new FixedText("Drinking water");
|
||||
this.elementsToShow = [
|
||||
new OperatorTag(),
|
||||
|
@ -47,10 +46,8 @@ export class DrinkingWater extends LayerDefinition {
|
|||
}
|
||||
|
||||
|
||||
private generateStyleFunction() {
|
||||
const self = this;
|
||||
return function (properties: any) {
|
||||
|
||||
private static generateStyleFunction() {
|
||||
return function () {
|
||||
return {
|
||||
color: "#00bb00",
|
||||
icon: {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {Tag} from "../../Logic/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Regex, Tag} from "../../Logic/TagsFilter";
|
||||
import {And, RegexTag, Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
export class GrbToFix extends LayerDefinition {
|
||||
|
@ -10,12 +10,12 @@ export class GrbToFix extends LayerDefinition {
|
|||
this.name = "grb";
|
||||
this.presets = [];
|
||||
this.icon = "./assets/star.svg";
|
||||
this.overpassFilter = new Regex("fixme", "GRB");
|
||||
this.overpassFilter = new RegexTag(/fixme/, /.*GRB.*/);
|
||||
this.minzoom = 13;
|
||||
|
||||
|
||||
|
||||
this.style = function (tags) {
|
||||
this.style = function () {
|
||||
return {
|
||||
icon: {
|
||||
iconUrl: "assets/star.svg",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {And, Tag} from "../../Logic/TagsFilter";
|
||||
import {And, Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
export class InformationBoard extends LayerDefinition {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {And, Tag} from "../../Logic/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
export class Map extends LayerDefinition {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {Or, Tag} from "../../Logic/Tags";
|
||||
import {AccessTag} from "../Questions/AccessTag";
|
||||
import {OperatorTag} from "../Questions/OperatorTag";
|
||||
import {NameQuestion} from "../Questions/NameQuestion";
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {AccessTag} from "../Questions/AccessTag";
|
||||
import {OperatorTag} from "../Questions/OperatorTag";
|
||||
import {Or, Tag} from "../../Logic/Tags";
|
||||
import {NameQuestion} from "../Questions/NameQuestion";
|
||||
import {NameInline} from "../Questions/NameInline";
|
||||
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {FixedUiElement} from "../../UI/Base/FixedUiElement";
|
||||
import FixedText from "../Questions/FixedText";
|
||||
import {Tag} from "../../Logic/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
|
@ -19,7 +18,7 @@ export class Viewpoint extends LayerDefinition {
|
|||
}],
|
||||
icon: "assets/viewpoint.svg",
|
||||
wayHandling: LayerDefinition.WAYHANDLING_CENTER_ONLY,
|
||||
style: tags => {
|
||||
style: _ => {
|
||||
return {
|
||||
color: undefined, icon: {
|
||||
iconUrl: "assets/viewpoint.svg",
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {And, Not, Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {Park} from "./Park";
|
||||
import {And, Or, Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
export class Widths extends LayerDefinition {
|
||||
|
||||
private cyclistWidth: number;
|
||||
private carWidth: number;
|
||||
private pedestrianWidth: number;
|
||||
private readonly cyclistWidth: number;
|
||||
private readonly carWidth: number;
|
||||
private readonly pedestrianWidth: number;
|
||||
|
||||
private readonly _bothSideParking = new Tag("parking:lane:both", "parallel");
|
||||
private readonly _noSideParking = new Tag("parking:lane:both", "no_parking");
|
||||
|
@ -36,10 +35,9 @@ export class Widths extends LayerDefinition {
|
|||
|
||||
private readonly _oneSideParking = new Or([this._leftSideParking, this._rightSideParking]);
|
||||
|
||||
private readonly _carfree = new Or(
|
||||
private readonly _carfree = new And(
|
||||
[new Tag("highway", "pedestrian"), new Tag("highway", "living_street"),
|
||||
new Tag("access","destination"), new Tag("motor_vehicle", "destination")])
|
||||
private readonly _notCarFree = new Not(this._carfree);
|
||||
|
||||
private calcProps(properties) {
|
||||
let parkingStateKnown = true;
|
||||
|
@ -59,8 +57,7 @@ export class Widths extends LayerDefinition {
|
|||
}
|
||||
|
||||
|
||||
let pedestrianFlowNeeded = 0;
|
||||
|
||||
let pedestrianFlowNeeded;
|
||||
if (this._sidewalkBoth.matchesProperties(properties)) {
|
||||
pedestrianFlowNeeded = 0;
|
||||
} else if (this._sidewalkNone.matchesProperties(properties)) {
|
||||
|
@ -198,7 +195,7 @@ export class Widths extends LayerDefinition {
|
|||
renderTemplate: "{note:width:carriageway}",
|
||||
template: "$$$",
|
||||
}
|
||||
}).OnlyShowIf(this._notCarFree),
|
||||
}).OnlyShowIf(this._carfree, true),
|
||||
|
||||
|
||||
new TagRenderingOptions({
|
||||
|
@ -218,7 +215,7 @@ export class Widths extends LayerDefinition {
|
|||
renderTemplate: "{note:width:carriageway}",
|
||||
template: "$$$",
|
||||
}
|
||||
}).OnlyShowIf(this._notCarFree),
|
||||
}).OnlyShowIf(this._carfree, true),
|
||||
|
||||
|
||||
new TagRenderingOptions({
|
||||
|
@ -248,7 +245,7 @@ export class Widths extends LayerDefinition {
|
|||
txt: "Tweerichtingsverkeer voor iedereen. Dit gebruikt <b>" + r(2 * this.carWidth + 2 * this.cyclistWidth) + "m</b>"
|
||||
}
|
||||
]
|
||||
}).OnlyShowIf(this._notCarFree),
|
||||
}).OnlyShowIf(this._carfree, true),
|
||||
|
||||
new TagRenderingOptions(
|
||||
{
|
||||
|
@ -266,7 +263,7 @@ export class Widths extends LayerDefinition {
|
|||
{k: new Tag("short",""), txt: "De totale nodige ruimte voor vlot en veilig verkeer is dus <span class='thanks'>{targetWidth}m</span>"}
|
||||
]
|
||||
}
|
||||
).OnlyShowIf(this._notCarFree),
|
||||
).OnlyShowIf(this._carfree, true),
|
||||
|
||||
|
||||
new TagRenderingOptions({
|
||||
|
|
|
@ -2,7 +2,6 @@ import {LayerDefinition} from "./LayerDefinition";
|
|||
import {UIElement} from "../UI/UIElement";
|
||||
import Translations from "../UI/i18n/Translations";
|
||||
import Combine from "../UI/Base/Combine";
|
||||
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||
import {State} from "../State";
|
||||
|
||||
/**
|
||||
|
@ -10,7 +9,7 @@ import {State} from "../State";
|
|||
*/
|
||||
export class Layout {
|
||||
|
||||
public name: string;
|
||||
public id: string;
|
||||
public icon: string = "./assets/logo.svg";
|
||||
public title: UIElement;
|
||||
public maintainer: string;
|
||||
|
@ -25,20 +24,19 @@ export class Layout {
|
|||
public welcomeBackMessage: UIElement;
|
||||
public welcomeTail: UIElement;
|
||||
|
||||
public startzoom: number;
|
||||
public supportedLanguages: string[];
|
||||
|
||||
public startzoom: number;
|
||||
public startLon: number;
|
||||
public startLat: number;
|
||||
|
||||
public locationContains: string[];
|
||||
|
||||
public enableAdd: boolean = true;
|
||||
public enableUserBadge: boolean = true;
|
||||
public enableSearch: boolean = true;
|
||||
public enableLayers: boolean = true;
|
||||
public enableMoreQuests: boolean = true;
|
||||
public enableShareScreen: boolean = true;
|
||||
|
||||
public enableGeolocation: boolean = true;
|
||||
public hideFromOverview: boolean = false;
|
||||
|
||||
/**
|
||||
|
@ -47,11 +45,10 @@ export class Layout {
|
|||
*/
|
||||
public widenFactor: number = 0.07;
|
||||
public defaultBackground: string = "osm";
|
||||
public enableGeolocation: boolean = true;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param name: The name used in the query string. If in the query "quests=<name>" is defined, it will select this layout
|
||||
* @param id: The name used in the query string. If in the query "quests=<name>" is defined, it will select this layout
|
||||
* @param title: Will be used in the <title> of the page
|
||||
* @param layers: The layers to show, a list of LayerDefinitions
|
||||
* @param startzoom: The initial starting zoom of the map
|
||||
|
@ -63,7 +60,7 @@ export class Layout {
|
|||
* @param welcomeTail: This text is shown below the login message. It is ideal for extra help
|
||||
*/
|
||||
constructor(
|
||||
name: string,
|
||||
id: string,
|
||||
supportedLanguages: string[],
|
||||
title: UIElement | string,
|
||||
layers: LayerDefinition[],
|
||||
|
@ -85,7 +82,7 @@ export class Layout {
|
|||
this.startLon = startLon;
|
||||
this.startLat = startLat;
|
||||
this.startzoom = startzoom;
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
this.layers = layers;
|
||||
this.welcomeMessage = Translations.W(welcomeMessage)
|
||||
this.gettingStartedPlzLogin = Translations.W(gettingStartedPlzLogin);
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
import {Layout} from "../Layout";
|
||||
|
||||
export class All extends Layout{
|
||||
constructor() {
|
||||
super(
|
||||
"all",
|
||||
["en"],
|
||||
"All quest layers",
|
||||
[],
|
||||
15,
|
||||
51.2,
|
||||
3.2,
|
||||
"<h3>All quests of MapComplete</h3>" +
|
||||
"This is a mixed bag. Some quests might be hard or for experts to answer only",
|
||||
"Please log in",
|
||||
""
|
||||
);
|
||||
this.hideFromOverview = true;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import {Layout} from "../Layout";
|
||||
import {ClimbingTree} from "../Layers/ClimbingTree";
|
||||
|
|
|
@ -29,6 +29,7 @@ export default class Cyclofix extends Layout {
|
|||
);
|
||||
this.icon = "./assets/bike/logo.svg"
|
||||
this.description = "Easily search and contribute bicycle data nearby";
|
||||
this.socialImage = "./assets/bike/cyclofix.jpeg"
|
||||
this.socialImage = "./assets/bike/cyclofix.jpeg";
|
||||
this.widenFactor = 0.5;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {Layout} from "../Layout";
|
||||
import {GhostBike} from "../Layers/GhostBike";
|
||||
import Combine from "../../UI/Base/Combine";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
|
||||
export class GhostBikes extends Layout {
|
||||
constructor() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {Layout} from "../Layout";
|
||||
import {LayerDefinition} from "../LayerDefinition";
|
||||
import {Or, Tag} from "../../Logic/TagsFilter";
|
||||
import {Or, Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor";
|
||||
import {TagsFilter, TagUtils} from "../Logic/Tags";
|
||||
import {UIElement} from "../UI/UIElement";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Translation from "../UI/i18n/Translation";
|
||||
|
||||
/**
|
||||
* Wrapper around another TagDependandElement, which only shows if the filters match
|
||||
*/
|
||||
import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor";
|
||||
import {TagsFilter, TagUtils} from "../Logic/TagsFilter";
|
||||
import {UIElement} from "../UI/UIElement";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {Changes} from "../Logic/Osm/Changes";
|
||||
|
||||
|
||||
export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{
|
||||
private _tagsFilter: TagsFilter;
|
||||
private _embedded: TagDependantUIElementConstructor;
|
||||
|
||||
constructor(tagsFilter : TagsFilter, embedded: TagDependantUIElementConstructor) {
|
||||
private readonly _tagsFilter: TagsFilter;
|
||||
private readonly _embedded: TagDependantUIElementConstructor;
|
||||
private readonly _invert: boolean;
|
||||
|
||||
constructor(tagsFilter: TagsFilter, embedded: TagDependantUIElementConstructor, invert: boolean = false) {
|
||||
this._tagsFilter = tagsFilter;
|
||||
this._embedded = embedded;
|
||||
this._invert = invert;
|
||||
}
|
||||
|
||||
construct(dependencies): TagDependantUIElement {
|
||||
return new OnlyShowIf(dependencies.tags,
|
||||
this._embedded.construct(dependencies),
|
||||
this._tagsFilter);
|
||||
this._tagsFilter,
|
||||
this._invert);
|
||||
}
|
||||
|
||||
IsKnown(properties: any): boolean {
|
||||
|
@ -41,34 +43,38 @@ export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{
|
|||
return this._embedded.Priority();
|
||||
}
|
||||
|
||||
GetContent(tags: any): string {
|
||||
if(!this.IsKnown(tags)){
|
||||
GetContent(tags: any): Translation {
|
||||
if(this.IsKnown(tags)){
|
||||
return undefined;
|
||||
}
|
||||
return this._embedded.GetContent(tags);
|
||||
}
|
||||
|
||||
private Matches(properties: any) : boolean{
|
||||
return this._tagsFilter.matches(TagUtils.proprtiesToKV(properties));
|
||||
return this._tagsFilter.matches(TagUtils.proprtiesToKV(properties)) != this._invert;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OnlyShowIf extends UIElement implements TagDependantUIElement {
|
||||
private _embedded: TagDependantUIElement;
|
||||
private _filter: TagsFilter;
|
||||
private readonly _embedded: TagDependantUIElement;
|
||||
private readonly _filter: TagsFilter;
|
||||
private readonly _invert: boolean;
|
||||
|
||||
constructor(
|
||||
tags: UIEventSource<any>,
|
||||
embedded: TagDependantUIElement, filter: TagsFilter) {
|
||||
embedded: TagDependantUIElement,
|
||||
filter: TagsFilter,
|
||||
invert: boolean) {
|
||||
super(tags);
|
||||
this._filter = filter;
|
||||
this._embedded = embedded;
|
||||
this._invert = invert;
|
||||
|
||||
}
|
||||
|
||||
private Matches() : boolean{
|
||||
return this._filter.matches(TagUtils.proprtiesToKV(this._source.data));
|
||||
return this._filter.matches(TagUtils.proprtiesToKV(this._source.data)) != this._invert;
|
||||
}
|
||||
|
||||
InnerRender(): string {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {Changes} from "../../Logic/Osm/Changes";
|
||||
import {And, Tag} from "../../Logic/TagsFilter";
|
||||
import {And, Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
export class AccessTag extends TagRenderingOptions {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {UIElement} from "../../UI/UIElement";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
import Translation from "../../UI/i18n/Translation";
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {And, Tag} from "../../Logic/TagsFilter";
|
||||
import {UIElement} from "../../UI/UIElement";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import Translations from "../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
import Translation from "../../UI/i18n/Translation";
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* One is a big 'name-question', the other is the 'edit name' in the title.
|
||||
* THis one is the big question
|
||||
*/
|
||||
import {Tag} from "../../Logic/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
export class NameQuestion extends TagRenderingOptions{
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {Changes} from "../../Logic/Osm/Changes";
|
||||
import {Tag} from "../../Logic/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {Img} from "../../UI/Img";
|
||||
import {Tag} from "../../Logic/TagsFilter";
|
||||
import {Tag} from "../../Logic/Tags";
|
||||
import {TagRenderingOptions} from "../TagRenderingOptions";
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Tag } from "../../../Logic/TagsFilter";
|
||||
import { Tag } from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Translations from "../../../UI/i18n/Translations";
|
||||
import Combine from "../../../UI/Base/Combine";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Tag } from "../../../Logic/TagsFilter";
|
||||
import { Tag } from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag, And} from "../../../Logic/TagsFilter";
|
||||
import {Tag, And} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import Combine from "../../../UI/Base/Combine";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
||||
/**
|
||||
* Currently not used in Cyclofix because it's a little vague
|
||||
*
|
||||
* TODO: Translations
|
||||
*/
|
||||
export default class BikeStationBrand extends TagRenderingOptions {
|
||||
private static options = {
|
||||
priority: 15,
|
||||
question: "What is the brand of this bike station (name of university, shop, city...)?",
|
||||
freeform: {
|
||||
key: "brand",
|
||||
template: "The brand of this bike station is $$$",
|
||||
renderTemplate: "The brand of this bike station is {operator}",
|
||||
placeholder: "brand"
|
||||
},
|
||||
mappings: [
|
||||
{k: new Tag("brand", "Velo Fix Station"), txt: "Velo Fix Station"}
|
||||
]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
throw Error('BikeStationBrand disabled')
|
||||
super(BikeStationBrand.options);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag, And} from "../../../Logic/TagsFilter";
|
||||
import {Tag, And} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Tag} from "../../../Logic/TagsFilter";
|
||||
import {Tag} from "../../../Logic/Tags";
|
||||
import Translations from "../../../UI/i18n/Translations";
|
||||
import {TagRenderingOptions} from "../../TagRenderingOptions";
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import {UIElement} from "../UI/UIElement";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import {And, Tag, TagsFilter, TagUtils} from "../Logic/TagsFilter";
|
||||
import {And, Tag, TagsFilter, TagUtils} from "../Logic/Tags";
|
||||
import {FixedUiElement} from "../UI/Base/FixedUiElement";
|
||||
import {SaveButton} from "../UI/SaveButton";
|
||||
import {VariableUiElement} from "../UI/Base/VariableUIElement";
|
||||
|
@ -15,23 +15,21 @@ import Locale from "../UI/i18n/Locale";
|
|||
import {State} from "../State";
|
||||
import {TagRenderingOptions} from "./TagRenderingOptions";
|
||||
import Translation from "../UI/i18n/Translation";
|
||||
import {SubtleButton} from "../UI/Base/SubtleButton";
|
||||
import Combine from "../UI/Base/Combine";
|
||||
|
||||
|
||||
export class TagRendering extends UIElement implements TagDependantUIElement {
|
||||
export class
|
||||
TagRendering extends UIElement implements TagDependantUIElement {
|
||||
|
||||
|
||||
private _priority: number;
|
||||
|
||||
|
||||
private _question: string | Translation;
|
||||
private _mapping: { k: TagsFilter, txt: string | UIElement, priority?: number }[];
|
||||
private readonly _priority: number;
|
||||
private readonly _question: string | Translation;
|
||||
private readonly _mapping: { k: TagsFilter, txt: string | UIElement, priority?: number }[];
|
||||
|
||||
private currentTags : UIEventSource<any> ;
|
||||
|
||||
|
||||
private _freeform: {
|
||||
private readonly _freeform: {
|
||||
key: string,
|
||||
template: string | UIElement,
|
||||
renderTemplate: string | Translation,
|
||||
|
@ -53,10 +51,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
private readonly _questionSkipped: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
|
||||
private readonly _editMode: UIEventSource<boolean> = new UIEventSource<boolean>(false);
|
||||
|
||||
|
||||
private static injected = TagRendering.injectFunction();
|
||||
|
||||
|
||||
static injectFunction() {
|
||||
// This is a workaround as not to import tagrendering into TagREnderingOptions
|
||||
TagRenderingOptions.tagRendering = (tags, options) => new TagRendering(tags, options);
|
||||
|
@ -76,7 +71,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
extraTags?: TagsFilter,
|
||||
},
|
||||
tagsPreprocessor?: ((tags: any) => any),
|
||||
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean }[]
|
||||
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[]
|
||||
}) {
|
||||
super(tags);
|
||||
this.ListenTo(Locale.language);
|
||||
|
@ -204,7 +199,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
placeholder?: string | Translation,
|
||||
extraTags?: TagsFilter,
|
||||
},
|
||||
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean }[]
|
||||
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[]
|
||||
}):
|
||||
InputElement<TagsFilter> {
|
||||
|
||||
|
@ -217,7 +212,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
if(mapping.k === null){
|
||||
continue;
|
||||
}
|
||||
if(previousTexts.indexOf(mapping.txt) >= 0){
|
||||
if(mapping.hideInAnswer){
|
||||
continue;
|
||||
}
|
||||
previousTexts.push(this.ApplyTemplate(mapping.txt));
|
||||
|
@ -262,7 +257,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
|
||||
let isValid = ValidatedTextField.inputValidation[type];
|
||||
if (isValid === undefined) {
|
||||
isValid = (str) => true;
|
||||
isValid = () => true;
|
||||
}
|
||||
let formatter = ValidatedTextField.formatting[type] ?? ((str) => str);
|
||||
|
||||
|
@ -297,7 +292,6 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
}
|
||||
|
||||
|
||||
let inputElement: InputElement<TagsFilter>;
|
||||
const textField = new TextField({
|
||||
placeholder: this._freeform.placeholder,
|
||||
fromString: pickString,
|
||||
|
@ -455,9 +449,9 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
|
|||
|
||||
private ApplyTemplate(template: string | Translation): UIElement {
|
||||
if (template === undefined || template === null) {
|
||||
throw "Trying to apply a template, but the template is null/undefined"
|
||||
console.warn("Applying template which is undefined by ",this); // TODO THis error msg can probably be removed
|
||||
return undefined;
|
||||
}
|
||||
const self = this;
|
||||
return new VariableUiElement(this.currentTags.map(tags => {
|
||||
const tr = Translations.WT(template);
|
||||
if (tr.Subs === undefined) {
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
import {Dependencies, TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor";
|
||||
import * as EmailValidator from "email-validator";
|
||||
import {parsePhoneNumberFromString} from "libphonenumber-js";
|
||||
import {UIElement} from "../UI/UIElement";
|
||||
import {TagsFilter, TagUtils} from "../Logic/TagsFilter";
|
||||
import {TagsFilter, TagUtils} from "../Logic/Tags";
|
||||
import {OnlyShowIfConstructor} from "./OnlyShowIf";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Translation from "../UI/i18n/Translation";
|
||||
import Translations from "../UI/i18n/Translations";
|
||||
|
||||
export class TagRenderingOptions implements TagDependantUIElementConstructor {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Notes: by not giving a 'question', one disables the question form alltogether
|
||||
*/
|
||||
|
||||
public options: {
|
||||
priority?: number;
|
||||
question?: string | Translation;
|
||||
|
@ -27,10 +21,9 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
|
|||
placeholder?: string | Translation;
|
||||
extraTags?: TagsFilter
|
||||
};
|
||||
mappings?: { k: TagsFilter; txt: string | Translation; priority?: number, substitute?: boolean }[]
|
||||
mappings?: { k: TagsFilter; txt: string | Translation; priority?: number, substitute?: boolean, hideInAnwser?: boolean }[]
|
||||
};
|
||||
|
||||
|
||||
constructor(options: {
|
||||
|
||||
|
||||
|
@ -61,7 +54,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
|
|||
*
|
||||
*
|
||||
*/
|
||||
mappings?: { k: TagsFilter, txt: Translation | string, priority?: number, substitute?: boolean }[],
|
||||
mappings?: { k: TagsFilter, txt: Translation | string, priority?: number, substitute?: boolean , hideInAnswer?:boolean}[],
|
||||
|
||||
|
||||
/**
|
||||
|
@ -85,12 +78,11 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
|
|||
*/
|
||||
tagsPreprocessor?: ((tags: any) => void)
|
||||
}) {
|
||||
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
OnlyShowIf(tagsFilter: TagsFilter): TagDependantUIElementConstructor {
|
||||
return new OnlyShowIfConstructor(tagsFilter, this);
|
||||
OnlyShowIf(tagsFilter: TagsFilter, invert: boolean = false): TagDependantUIElementConstructor {
|
||||
return new OnlyShowIfConstructor(tagsFilter, this, invert);
|
||||
}
|
||||
|
||||
|
||||
|
@ -105,39 +97,28 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
|
|||
if (this.options.freeform !== undefined && tags[this.options.freeform.key] !== undefined) {
|
||||
return false;
|
||||
}
|
||||
if (this.options.question === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return this.options.question !== undefined;
|
||||
}
|
||||
|
||||
GetContent(tags: any): string {
|
||||
GetContent(tags: any): Translation {
|
||||
const tagsKV = TagUtils.proprtiesToKV(tags);
|
||||
|
||||
for (const oneOnOneElement of this.options.mappings ?? []) {
|
||||
if (oneOnOneElement.k === null || oneOnOneElement.k.matches(tagsKV)) {
|
||||
const mapping = oneOnOneElement.txt;
|
||||
if (typeof (mapping) === "string") {
|
||||
return mapping;
|
||||
} else {
|
||||
return mapping.InnerRender();
|
||||
}
|
||||
return Translations.WT(oneOnOneElement.txt);
|
||||
}
|
||||
}
|
||||
if (this.options.freeform !== undefined) {
|
||||
let template = this.options.freeform.renderTemplate;
|
||||
if (typeof (template) !== "string") {
|
||||
template = template.InnerRender();
|
||||
}
|
||||
return TagUtils.ApplyTemplate(template, tags);
|
||||
let template = Translations.WT(this.options.freeform.renderTemplate);
|
||||
return template.Subs(tags);
|
||||
}
|
||||
|
||||
console.warn("No content defined for",tags," with mapping",this);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
public static tagRendering: (tags: UIEventSource<any>, options: { priority?: number; question?: string | Translation; freeform?: { key: string; tagsPreprocessor?: (tags: any) => any; template: string | Translation; renderTemplate: string | Translation; placeholder?: string | Translation; extraTags?: TagsFilter }; mappings?: { k: TagsFilter; txt: string | Translation; priority?: number; substitute?: boolean }[] }) => TagDependantUIElement;
|
||||
public static tagRendering: (tags: UIEventSource<any>, options: { priority?: number; question?: string | Translation; freeform?: { key: string; tagsPreprocessor?: (tags: any) => any; template: string | Translation; renderTemplate: string | Translation; placeholder?: string | Translation; extraTags?: TagsFilter }; mappings?: { k: TagsFilter; txt: string | Translation; priority?: number; substitute?: boolean, hideInAnswer?: boolean }[] }) => TagDependantUIElement;
|
||||
|
||||
construct(dependencies: Dependencies): TagDependantUIElement {
|
||||
return TagRenderingOptions.tagRendering(dependencies.tags, this.options);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {UIElement} from "../UI/UIElement";
|
||||
import {UIEventSource} from "../Logic/UIEventSource";
|
||||
import Translation from "../UI/i18n/Translation";
|
||||
|
||||
|
||||
export interface Dependencies {
|
||||
|
@ -12,7 +13,7 @@ export interface TagDependantUIElementConstructor {
|
|||
IsKnown(properties: any): boolean;
|
||||
IsQuestioning(properties: any): boolean;
|
||||
Priority(): number;
|
||||
GetContent(tags: any): string;
|
||||
GetContent(tags: any): Translation;
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue