Further work, add checkboxes as option

This commit is contained in:
Pieter Vander Vennet 2020-09-10 19:33:06 +02:00
parent 8bb9b1228b
commit c944156d87
28 changed files with 364 additions and 149 deletions

View file

@ -9,8 +9,6 @@ import {LayerConfigJson} from "./LayerConfigJson";
import {LayerDefinition, Preset} from "../LayerDefinition";
import {TagDependantUIElementConstructor} from "../UIElementConstructor";
import Combine from "../../UI/Base/Combine";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import {ImageCarouselConstructor} from "../../UI/Image/ImageCarousel";
import * as drinkingWater from "../../assets/layers/drinking_water/drinking_water.json";
import * as ghostbikes from "../../assets/layers/ghost_bike/ghost_bike.json"
import * as viewpoint from "../../assets/layers/viewpoint/viewpoint.json"
@ -20,12 +18,19 @@ import * as birdhides from "../../assets/layers/bird_hide/birdhides.json"
import * as nature_reserve from "../../assets/layers/nature_reserve/nature_reserve.json"
import {Utils} from "../../Utils";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import {ImageCarouselConstructor} from "../../UI/Image/ImageCarousel";
import {State} from "../../State";
export class FromJSON {
public static sharedLayers: Map<string, LayerDefinition> = FromJSON.getSharedLayers();
private static getSharedLayers() {
// We inject a function into state while we are busy
State.FromBase64 = FromJSON.FromBase64;
const sharedLayers = new Map<string, LayerDefinition>();
const sharedLayersList = [
@ -50,6 +55,7 @@ export class FromJSON {
}
public static LayoutFromJSON(json: LayoutConfigJson): Layout {
console.log(json)
const tr = FromJSON.Translation;
const layers = json.layers.map(FromJSON.Layer);
@ -111,7 +117,6 @@ export class FromJSON {
if (typeof json === "string") {
switch (json) {
case "picture": {
return new ImageCarouselWithUploadConstructor()
@ -123,7 +128,7 @@ export class FromJSON {
return new ImageCarouselWithUploadConstructor()
}
case "images": {
return new ImageCarouselWithUploadConstructor()
return new ImageCarouselWithUploadConstructor()
}
case "picturesNoUpload": {
return new ImageCarouselConstructor()
@ -193,11 +198,13 @@ export class FromJSON {
let rendering = new TagRenderingOptions({
question: question,
freeform: freeform,
mappings: mappings
mappings: mappings,
multiAnswer: json.multiAnswer
});
if (json.condition) {
return rendering.OnlyShowIf(FromJSON.Tag(json.condition, `In tagrendering ${propertyName}.condition`));
const condition = FromJSON.Tag(json.condition, `In tagrendering ${propertyName}.condition`);
return rendering.OnlyShowIf(condition);
}
return rendering;

View file

@ -38,7 +38,12 @@ export interface TagRenderingConfigJson {
* Usefull to add a 'fixme=freeform textfield used - to be checked'
**/
addExtraTags?: 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

View file

@ -1,6 +1,6 @@
import {LayerDefinition} from "../LayerDefinition";
import FixedText from "../Questions/FixedText";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import Translations from "../../UI/i18n/Translations";
import CafeName from "../Questions/bike/CafeName";
import {And, Or, RegexTag, Tag} from "../../Logic/Tags";

View file

@ -1,7 +1,7 @@
import {LayerDefinition} from "../LayerDefinition";
import Translations from "../../UI/i18n/Translations";
import {And, RegexTag, Tag} from "../../Logic/Tags";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import ShopRetail from "../Questions/bike/ShopRetail";
import ShopPump from "../Questions/bike/ShopPump";
import ShopRental from "../Questions/bike/ShopRental";

View file

@ -1,7 +1,7 @@
import {LayerDefinition} from "../LayerDefinition";
import Translations from "../../UI/i18n/Translations";
import {And, Tag} from "../../Logic/Tags";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import ShopRetail from "../Questions/bike/ShopRetail";
import ShopPump from "../Questions/bike/ShopPump";
import ShopRental from "../Questions/bike/ShopRental";

View file

@ -5,7 +5,7 @@ import {OperatorTag} from "../Questions/OperatorTag";
import {NameQuestion} from "../Questions/NameQuestion";
import {NameInline} from "../Questions/NameInline";
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
export class Bos extends LayerDefinition {

View file

@ -1,8 +1,7 @@
import {LayerDefinition} from "../LayerDefinition";
import FixedText from "../Questions/FixedText";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import {And, Tag} from "../../Logic/Tags";
import {TagRenderingOptions} from "../TagRenderingOptions";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
export class InformationBoard extends LayerDefinition {
constructor() {

View file

@ -1,6 +1,6 @@
import {LayerDefinition} from "../LayerDefinition";
import FixedText from "../Questions/FixedText";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import {Tag} from "../../Logic/Tags";
import {TagRenderingOptions} from "../TagRenderingOptions";

View file

@ -5,7 +5,7 @@ import {OperatorTag} from "../Questions/OperatorTag";
import {NameQuestion} from "../Questions/NameQuestion";
import {NameInline} from "../Questions/NameInline";
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import {TagRenderingOptions} from "../TagRenderingOptions";
export class NatureReserves extends LayerDefinition {

View file

@ -3,7 +3,7 @@ import {Or, Tag} from "../../Logic/Tags";
import {NameQuestion} from "../Questions/NameQuestion";
import {NameInline} from "../Questions/NameInline";
import {DescriptionQuestion} from "../Questions/DescriptionQuestion";
import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload";
import ImageCarouselWithUploadConstructor from "../../UI/Image/ImageCarouselWithUpload";
import {TagRenderingOptions} from "../TagRenderingOptions";
export class Park extends LayerDefinition {

View file

@ -21,6 +21,7 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
placeholder?: string | Translation;
extraTags?: TagsFilter
};
multiAnswer?: boolean,
mappings?: { k: TagsFilter; txt: string | Translation; priority?: number, substitute?: boolean, hideInAnwser?: boolean }[]
};
@ -54,8 +55,12 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
*
*
*/
mappings?: { k: TagsFilter, txt: Translation | string, priority?: number, substitute?: boolean , hideInAnswer?:boolean}[],
mappings?: { k: TagsFilter, txt: Translation | string, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[],
/**
* If true, use checkboxes to answer instead of radiobuttons
*/
multiAnswer?: boolean,
/**
* If one wants to render a freeform tag (thus no predefined key/values) or if there are a few well-known tags with a freeform object,
@ -113,12 +118,25 @@ export class TagRenderingOptions implements TagDependantUIElementConstructor {
return template.Subs(tags);
}
console.warn("No content defined for",tags," with mapping",this);
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, hideInAnswer?: 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
},
multiAnswer?: boolean,
mappings?: { k: TagsFilter; txt: string | Translation; priority?: number; substitute?: boolean, hideInAnswer?: boolean }[]
}) => TagDependantUIElement;
construct(dependencies: Dependencies): TagDependantUIElement {
return TagRenderingOptions.tagRendering(dependencies.tags, this.options);

View file

@ -16,6 +16,7 @@ export class PersonalLayersPanel extends UIElement {
super(State.state.favouriteLayers);
this.ListenTo(State.state.osmConnection.userDetails);
this.ListenTo(State.state.favouriteLayers);
this.UpdateView([]);
const self = this;
@ -53,6 +54,9 @@ export class PersonalLayersPanel extends UIElement {
this.checkboxes.push(header);
for (const layer of layout.layers) {
if(typeof layer === "string"){
continue;
}
let icon = layer.icon;
if (icon !== undefined && typeof (icon) !== "string") {
icon = icon.GetContent({"id": "node/-1"}).txt ?? "./assets/bug.svg";

View file

@ -344,5 +344,133 @@ export class TagUtils {
}
return properties;
}
/**
* Given multiple tagsfilters which can be used as answer, will take the tags with the same keys together as set.
* E.g:
*
* FlattenMultiAnswer([and: [ "x=a", "y=0;1"], and: ["x=b", "y=2"], and: ["x=", "y=3"]])
* will result in
* ["x=a;b", "y=0;1;2;3"]
*
* @param tagsFilters
* @constructor
*/
static FlattenMultiAnswer(tagsFilters: TagsFilter[]): And {
if (tagsFilters === undefined) {
return new And([]);
}
const keyValues = {} // Map string -> string[]
tagsFilters = [...tagsFilters]
while (tagsFilters.length > 0) {
const tagsFilter = tagsFilters.pop();
if (tagsFilter === undefined) {
continue;
}
if (tagsFilter instanceof And) {
tagsFilters.push(...tagsFilter.and);
continue;
}
if (tagsFilter instanceof Tag) {
if (keyValues[tagsFilter.key] === undefined) {
keyValues[tagsFilter.key] = [];
}
keyValues[tagsFilter.key].push(...tagsFilter.value.split(";"));
continue;
}
console.error("Invalid type to flatten the multiAnswer", tagsFilter);
throw "Invalid type to FlattenMultiAnswer"
}
const and: TagsFilter[] = []
for (const key in keyValues) {
and.push(new Tag(key, Utils.Dedup(keyValues[key]).join(";")));
}
return new And(and);
}
/**
* Splits the actualTags onto a list of which the values are the same as the tagsFilters.
* Leftovers are returned in the list too if there is an 'undefined' value
*/
static SplitMultiAnswer(actualTags: TagsFilter, possibleTags: TagsFilter[], freeformKey: string, freeformExtraTags: TagsFilter): TagsFilter[] {
const queue: TagsFilter[] = [actualTags]
const keyValues = {} // key ==> value[]
while (queue.length > 0) {
const tf = queue.pop();
if (tf instanceof And) {
queue.push(...tf.and);
continue;
}
if (tf instanceof Tag) {
if (keyValues[tf.key] === undefined) {
keyValues[tf.key] = []
}
keyValues[tf.key].push(...tf.value.split(";"));
continue;
}
if (tf === undefined) {
continue;
}
throw "Invalid tagfilter: " + JSON.stringify(tf)
}
const foundValues = [];
for (const possibleTag of possibleTags) {
if (possibleTag === undefined) {
continue;
}
if (possibleTag instanceof Tag) {
const key = possibleTag.key;
const actualValues: string[] = keyValues[key] ?? [];
const possibleValues = possibleTag.value.split(";");
let allPossibleValuesFound = true;
for (const possibleValue of possibleValues) {
if (actualValues.indexOf(possibleValue) < 0) {
allPossibleValuesFound = false;
}
}
if (!allPossibleValuesFound) {
continue;
}
// At this point, we know that 'possibleTag' is completely present in the tagset
// we add the possibleTag to the found values
foundValues.push(possibleTag);
for (const possibleValue of possibleValues) {
actualValues.splice(actualValues.indexOf(possibleValue), 1);
}
continue;
}
throw "Unsupported possibletag: " + JSON.stringify(possibleTag);
}
let leftoverTag = undefined;
if (keyValues[freeformKey] !== undefined && keyValues[freeformKey].length !== 0) {
leftoverTag = new Tag(freeformKey, keyValues[freeformKey].join(";"));
if (freeformExtraTags !== undefined) {
leftoverTag = new And([
leftoverTag,
freeformExtraTags
])
}
foundValues.push(leftoverTag);
}
return foundValues;
}
}

View file

@ -69,7 +69,7 @@ export class UIEventSource<T>{
if(g !== undefined) {
newSource.addCallback((latest) => {
self.setData((g(latest)));
self.setData(g(latest));
})
}

View file

@ -193,7 +193,7 @@ export class State {
continue;
}
try {
const layout = FromJSON.FromBase64(customLayout.data);
const layout = State.FromBase64(customLayout.data);
if(layout.id === undefined){
// This is an old style theme
// We remove it
@ -252,4 +252,6 @@ export class State {
}
}
public static FromBase64 : (data: string) => Layout = undefined;
}

View file

@ -39,7 +39,7 @@ export default class TagRenderingPreview extends UIElement {
try {
rendering =
new VariableUiElement(es.map(tagRenderingConfig => {
const tr = FromJSON.TagRendering(tagRenderingConfig)
const tr = FromJSON.TagRendering(tagRenderingConfig, "preview")
.construct({tags: this.previewTagValue});
return tr.Render();
}

View file

@ -9,7 +9,8 @@ import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler";
import {State} from "../../State";
import Translation from "../i18n/Translation";
export class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{
export default class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{
IsKnown(properties: any): boolean {
return true;
}

View file

@ -11,8 +11,8 @@ export class ImgurImage extends UIElement {
* Dictionary from url to alreayd known license info
*/
static allLicenseInfos: any = {};
private _imageMeta: UIEventSource<LicenseInfo>;
private _imageLocation: string;
private readonly _imageMeta: UIEventSource<LicenseInfo>;
private readonly _imageLocation: string;
constructor(source: string) {
super(undefined)

View file

@ -7,8 +7,8 @@ export class WikimediaImage extends UIElement {
static allLicenseInfos: any = {};
private _imageMeta: UIEventSource<LicenseInfo>;
private _imageLocation : string;
private readonly _imageMeta: UIEventSource<LicenseInfo>;
private readonly _imageLocation : string;
constructor(source: string) {
super(undefined)

View file

@ -13,17 +13,16 @@ export class CheckBoxes<T> extends InputElement<T[]> {
private readonly value: UIEventSource<T[]>;
private readonly _elements: InputElement<T>[]
private readonly _selectFirstAsDefault: boolean;
constructor(elements: InputElement<T>[],
selectFirstAsDefault = true) {
constructor(elements: InputElement<T>[]) {
super(undefined);
this._elements = Utils.NoNull(elements);
this._selectFirstAsDefault = selectFirstAsDefault;
this.dumbMode = false;
this.value = new UIEventSource<T[]>([])
this.ListenTo(this.value);
this.value.addCallback(latest => console.log("Latest is ", latest))
}
@ -35,6 +34,7 @@ export class CheckBoxes<T> extends InputElement<T[]> {
let matchFound = false;
for (const element of this._elements) {
if (element.IsValid(t)) {
element.GetValue().setData(t);
matchFound = true;
break
}
@ -56,7 +56,6 @@ export class CheckBoxes<T> extends InputElement<T[]> {
}
InnerRender(): string {
let body = "";
for (let i = 0; i < this._elements.length; i++) {
let el = this._elements[i];
@ -66,27 +65,30 @@ export class CheckBoxes<T> extends InputElement<T[]> {
}
return `<form id='${this.id}-form'>${body}</form>`;
return `<form id='${this.id}'>${body}</form>`;
}
protected InnerUpdate(htmlElement: HTMLElement) {
super.InnerUpdate(htmlElement);
const self = this;
for (let i = 0; i < this._elements.length; i++) {
const el = document.getElementById(this.IdFor(i));
const inputEl = this._elements[i];
{
const v = inputEl.GetValue().data;
const index = self.value.data.indexOf(v);
if(index >= 0){
// @ts-ignore
el.checked = true;
for (const t of this.value.data ?? []) {
if(t === undefined){
continue;
}
let isValid = inputEl.IsValid(t);
// @ts-ignore
el.checked = isValid;
if(isValid){
break;
}
}
el.onchange = e => {
const v = inputEl.GetValue().data;
const index = self.value.data.indexOf(v);

View file

@ -0,0 +1,53 @@
import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
export default class InputElementMap<T, X> extends InputElement<X> {
private readonly _inputElement: InputElement<T>;
private isSame: (x0: X, x1: X) => boolean;
private readonly fromX: (x: X) => T;
private readonly toX: (t: T) => X;
private readonly _value: UIEventSource<X>;
constructor(inputElement: InputElement<T>,
isSame: (x0: X, x1: X) => boolean,
toX: (t: T) => X,
fromX: (x: X) => T
) {
super();
this.isSame = isSame;
this.fromX = fromX;
this.toX = toX;
this._inputElement = inputElement;
this.IsSelected = inputElement.IsSelected;
const self = this;
this._value = inputElement.GetValue().map(
(t => {
const currentX = self.GetValue()?.data;
const newX = toX(t);
if (isSame(currentX, newX)) {
return currentX;
}
return newX;
}), [], x => {
const newT = fromX(x);
return newT;
});
}
GetValue(): UIEventSource<X> {
return this._value;
}
InnerRender(): string {
return this._inputElement.InnerRender();
}
IsSelected: UIEventSource<boolean>;
IsValid(x: X): boolean {
return this._inputElement.IsValid(this.fromX(x));
}
}

View file

@ -1,22 +1,23 @@
import {UIElement} from "../UI/UIElement";
import {UIEventSource} from "../Logic/UIEventSource";
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";
import {TagDependantUIElement} from "./UIElementConstructor";
import {TextField, ValidatedTextField} from "../UI/Input/TextField";
import {InputElement} from "../UI/Input/InputElement";
import {InputElementWrapper} from "../UI/Input/InputElementWrapper";
import {FixedInputElement} from "../UI/Input/FixedInputElement";
import {RadioButton} from "../UI/Input/RadioButton";
import Translations from "../UI/i18n/Translations";
import Locale from "../UI/i18n/Locale";
import {State} from "../State";
import {TagRenderingOptions} from "./TagRenderingOptions";
import Translation from "../UI/i18n/Translation";
import Combine from "../UI/Base/Combine";
import {TagDependantUIElement} from "../Customizations/UIElementConstructor";
import {UIElement} from "./UIElement";
import {VariableUiElement} from "./Base/VariableUIElement";
import InputElementMap from "./Input/InputElementMap";
import {CheckBoxes} from "./Input/Checkboxes";
import {InputElement} from "./Input/InputElement";
import {SaveButton} from "./SaveButton";
import {RadioButton} from "./Input/RadioButton";
import {InputElementWrapper} from "./Input/InputElementWrapper";
import {FixedInputElement} from "./Input/FixedInputElement";
import {TextField, ValidatedTextField} from "./Input/TextField";
import {TagRenderingOptions} from "../Customizations/TagRenderingOptions";
import {FixedUiElement} from "./Base/FixedUiElement";
export class TagRendering extends UIElement implements TagDependantUIElement {
@ -61,7 +62,6 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
priority?: number
question?: string | Translation,
freeform?: {
key: string,
template: string | Translation,
@ -70,6 +70,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
extraTags?: TagsFilter,
},
tagsPreprocessor?: ((tags: any) => any),
multiAnswer?: boolean,
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[]
}) {
super(tags);
@ -122,11 +123,10 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
});
}
// Prepare the actual input element -> pick an appropriate implementation
this._questionElement = this.InputElementFor(options) ??
new FixedInputElement<TagsFilter>("<span class='alert'>No input possible</span>", new Tag("a","b"));
new FixedInputElement<TagsFilter>("<span class='alert'>No input possible</span>", new Tag("a", "b"));
const save = () => {
const selection = self._questionElement.GetValue().data;
console.log("Tagrendering: saving tags ", selection);
@ -193,59 +193,93 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
private InputElementFor(options: {
freeform?: {
key: string,
key: string,
template: string | Translation,
renderTemplate: string | Translation,
placeholder?: string | Translation,
extraTags?: TagsFilter,
},
multiAnswer?: boolean,
mappings?: { k: TagsFilter, txt: string | Translation, priority?: number, substitute?: boolean, hideInAnswer?: boolean }[]
}):
InputElement<TagsFilter> {
const elements = [];
let freeformElement = undefined;
let freeformElement = undefined;
if (options.freeform !== undefined) {
freeformElement = this.InputForFreeForm(options.freeform);
}
if (options.mappings !== undefined) {
const previousTexts= [];
for (const mapping of options.mappings) {
if(mapping.k === null){
continue;
}
if(mapping.hideInAnswer){
continue;
}
previousTexts.push(this.ApplyTemplate(mapping.txt));
elements.push(this.InputElementForMapping(mapping, mapping.substitute));
}
if (options.mappings === undefined || options.mappings.length === 0) {
return freeformElement;
}
if(freeformElement !== undefined) {
const elements: InputElement<TagsFilter>[] = [];
for (const mapping of options.mappings) {
if (mapping.k === null) {
continue;
}
if (mapping.hideInAnswer) {
continue;
}
elements.push(this.InputElementForMapping(mapping, mapping.substitute));
}
if (freeformElement !== undefined) {
elements.push(freeformElement);
}
if (options.multiAnswer) {
const possibleTags = elements.map(el => el.GetValue().data);
const checkBoxes = new CheckBoxes(elements);
// In order to let this work, we are cheating a lot here
// First of all, it is very tricky to let the mapping stabilize
// The selection gets added as list and needs to be flattened into a new 'and'
// This new and causes the mapping to have a 'set value', which is unpacked to update the UI
// AFter which the UI reupdates and reapplies the value
// So, instead we opt to _always return the 'value' below which is statefully updated
// But, then we still have to figure out when to update...
// For this, we use the original inputElement
// This is very dirty code, I know
const value = new And([]);
const inputElement = new InputElementMap(checkBoxes,
(t0: And, t1: And) => {
return t0?.isEquivalent(t1) ?? t0 === t1
},
(fromUI) => {
if (fromUI === undefined) {
value.and = [];
return value;
}
const flattened = TagUtils.FlattenMultiAnswer(fromUI);
value.and = flattened.and;
return value;
},
(fromTags) => {
return TagUtils.SplitMultiAnswer(fromTags, possibleTags, this._freeform?.key, this._freeform?.extraTags);
}
);
let previousSelectionCount = -1;
checkBoxes.GetValue().addCallback(selected => {
const newSelectionCount = selected.length;
if (newSelectionCount != previousSelectionCount) {
previousSelectionCount = newSelectionCount;
inputElement.GetValue().ping();
}
});
return inputElement;
if (elements.length == 0) {
return new FixedInputElement("This should not happen: no tag renderings defined", undefined);
}
if (elements.length == 1) {
return elements[0];
}
return new RadioButton(elements, false);
}
private InputElementForMapping(mapping: { k: TagsFilter, txt: (string | Translation) }, substituteValues: boolean) {
private InputElementForMapping(mapping: { k: TagsFilter, txt: (string | Translation) }, substituteValues: boolean): FixedInputElement<TagsFilter> {
if (substituteValues) {
return new FixedInputElement(this.ApplyTemplate(mapping.txt),
@ -253,7 +287,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
(t0, t1) => t0.isEquivalent(t1)
);
}
return new FixedInputElement(this.ApplyTemplate(mapping.txt),mapping.k,
return new FixedInputElement(this.ApplyTemplate(mapping.txt), mapping.k,
(t0, t1) => t0.isEquivalent(t1));
}
@ -308,6 +342,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
const toString =
(tag) => {
console.log("Decoding ", tag, "in freeform text element")
if (tag instanceof And) {
for (const subtag of tag.and) {
if(subtag instanceof Tag && subtag.key === freeform.key){
@ -497,7 +532,7 @@ export class TagRendering extends UIElement implements TagDependantUIElement {
return tr.InnerRender();
}
return tr.Subs(tags).InnerRender()
}));
})).ListenTo(Locale.language);
}

View file

@ -238,46 +238,38 @@
"gl": "Esta bomba de ar admite as seguintes válvulas: {valves}"
},
"freeform": {
"addExtraTags": [
"#addExtraTags": [
"fixme=Freeform 'valves'-tag used: possibly a wrong value"
],
"key": "valves"
},
"multiAnswer": true,
"mappings": [
{
"if": "valves=sclaverand;schrader;dunlop",
"then": {
"en": "There is a default head, so Dunlop, Sclaverand and auto",
"nl": "Er is een standaard aansluiting, die dus voor Dunlop, Sclaverand en auto's",
"fr": "Il y a une valve par défaut, fonctionnant sur les valves Dunlop, Sclaverand et les valves de voitures",
"gl": "Hai un cabezal predeterminado que é compatíbel con Dunlop, Sclaverand e automóbil"
}
},
{
"if": "valves=sclaverand",
"then": {
"en": "Only Sclaverand (also known as Presta)",
"nl": "Enkel Sclaverand (ook gekend als Presta)",
"fr": "Seulement Sclaverand (aussi appelé Presta)",
"gl": "Só Sclaverand (tamén coñecido como Presta)"
"en": "Sclaverand (also known as Presta)",
"nl": "Sclaverand (ook gekend als Presta)",
"fr": "Sclaverand (aussi appelé Presta)",
"gl": "Sclaverand (tamén coñecido como Presta)"
}
},
{
"if": "valves=dunlop",
"then": {
"en": "Only Dunlop",
"nl": "Enkel Dunlop",
"fr": "Seulement Dunlop",
"gl": "Dunlop"
"en": "Dunlop",
"nl": "Dunlop",
"fr": "Dunlop",
"gl": "Dunlop"
}
},
{
"if": "valves=schrader",
"then": {
"en": "Only for cars",
"nl": "Enkel voor auto's",
"fr": "Seuelement les valves de voitures",
"gl": "Só para automóbiles"
"en": "Schrader (cars)",
"nl": "Schrader (auto's)",
"fr": "Schrader (les valves de voitures)",
"gl": "Schrader (para automóbiles)"
}
}
]

View file

@ -159,21 +159,13 @@
},
"mappings": [
{
"if": {
"and": [
"operator=Natuurpunt"
]
},
"if": "operator=Natuurpunt",
"then": {
"nl": "Beheer door Natuurpunt"
}
},
{
"if": {
"and": [
"operator=Agentschap Natuur en Bos"
]
},
"if": "operator=Agentschap Natuur en Bos",
"then": {
"nl": "Beheer door het Agentschap Natuur en Bos "
}

View file

@ -2,14 +2,15 @@ import {UIElement} from "./UI/UIElement";
// We HAVE to mark this while importing
UIElement.runningFromConsole = true;
import {TagRendering} from "./Customizations/TagRendering";
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import {FromJSON} from "./Customizations/JSON/FromJSON";
import {Layout} from "./Customizations/Layout";
import {readFileSync, writeFile, writeFileSync} from "fs";
import Locale from "./UI/i18n/Locale";
import svg2img from 'promise-svg2img';
import Translation from "./UI/i18n/Translation";
import Translations from "./UI/i18n/Translations";
import {TagRendering} from "./UI/TagRendering";
TagRendering.injectFunction();

View file

@ -1,6 +1,6 @@
import {UIEventSource} from "./Logic/UIEventSource";
import {GenerateEmpty} from "./UI/CustomGenerator/GenerateEmpty";
import {TagRendering} from "./Customizations/TagRendering";
import {TagRendering} from "./UI/TagRendering";
import {LayoutConfigJson} from "./Customizations/JSON/LayoutConfigJson";
import {OsmConnection} from "./Logic/Osm/OsmConnection";
import CustomGeneratorPanel from "./UI/CustomGenerator/CustomGeneratorPanel";

View file

@ -20,27 +20,4 @@ fi
git add . && git commit -m "New mapcomplete version" && git push
cd -
# clean up the mess we made
# rm *.js
# rm Logic/*.js
# rm Logic/*.js
# rm Logic/*/*.js
# rm Logic/*/*/*.js
# rm UI/*.js
# rm UI/*/*.js
# rm UI/*/*/*.js
# rm Customizations/*.js
# rm Customizations/*/*.js
# rm Customizations/*/*/*.js
rm *.webmanifest
rm assets/generated/*
for f in ./*.html; do
if [[ "$f" == "./index.html" ]] || [[ "$f" == "./land.html" ]] || [[ "$f" == "./test.html" ]] || [[ "$f" == "./preferences.html" ]] || [[ "$f" == "./customGenerator.html" ]]
then
echo "Not removing $f"
else
rm $f
fi
done
./clean.sh

View file

@ -1,4 +1,3 @@
import {TagRendering} from "./Customizations/TagRendering";
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import {Layout} from "./Customizations/Layout";
import {FixedUiElement} from "./UI/Base/FixedUiElement";
@ -7,6 +6,7 @@ import {QueryParameters} from "./Logic/Web/QueryParameters";
import {UIEventSource} from "./Logic/UIEventSource";
import * as $ from "jquery";
import {FromJSON} from "./Customizations/JSON/FromJSON";
import {TagRendering} from "./UI/TagRendering";
TagRendering.injectFunction();
@ -63,7 +63,6 @@ let layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?
const userLayoutParam = QueryParameters.GetQueryParameter("userlayout", "false");
const layoutFromBase64 = decodeURIComponent(userLayoutParam.data);
console.log(layoutFromBase64);
if (layoutFromBase64.startsWith("wiki:")) {
console.log("Downloading map theme from the wiki");
const themeName = layoutFromBase64.substr("wiki:".length);