From 14930e2f93a2679489f874965f4051ee6fb745b3 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sat, 8 Aug 2020 17:50:43 +0200 Subject: [PATCH] Add the possibility to load a custom layout with base64-encoded jsons --- Customizations/JSON/CustomLayoutFromJSON.ts | 221 ++++++++++++++++++++ Customizations/JSON/TagRenderingParser.ts | 24 --- Customizations/LayerDefinition.ts | 9 +- InitUiElements.ts | 2 +- UI/Img.ts | 15 ++ assets/help.svg | 49 +---- clean.sh | 2 +- index.css | 7 + index.ts | 14 +- preferences.html | 27 +++ 10 files changed, 296 insertions(+), 74 deletions(-) create mode 100644 Customizations/JSON/CustomLayoutFromJSON.ts delete mode 100644 Customizations/JSON/TagRenderingParser.ts create mode 100644 preferences.html diff --git a/Customizations/JSON/CustomLayoutFromJSON.ts b/Customizations/JSON/CustomLayoutFromJSON.ts new file mode 100644 index 0000000..34f52db --- /dev/null +++ b/Customizations/JSON/CustomLayoutFromJSON.ts @@ -0,0 +1,221 @@ +import {TagRenderingOptions} from "../TagRenderingOptions"; +import {LayerDefinition, Preset} from "../LayerDefinition"; +import {Layout} from "../Layout"; +import Translation from "../../UI/i18n/Translation"; +import {type} from "os"; +import Combine from "../../UI/Base/Combine"; +import {UIElement} from "../../UI/UIElement"; +import {And, Tag, TagsFilter} from "../../Logic/TagsFilter"; +import FixedText from "../Questions/FixedText"; +import {ImageCarouselWithUploadConstructor} from "../../UI/Image/ImageCarouselWithUpload"; + + +export class CustomLayoutFromJSON { + + public static exampleLayer = { + id: "bookcase", + icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaWQ9InN2ZzExMzgyIgogICBoZWlnaHQ9IjkwMCIKICAgd2lkdGg9IjkwMCIKICAgdmVyc2lvbj0iMS4wIj4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGExMCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGRlZnMKICAgICBpZD0iZGVmczExMzg0IiAvPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjkwMTAzMjU4LDAsMCwwLjkwMTAzMjU4LDExMi44NDA1OCwtMS45MDYwMTc3KSI+CiAgICA8ZwogICAgICAgaWQ9ImcxMTQ3NiI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMTE0NzIiCiAgICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjEyMDEuOTI0OTI2NzZweDtmb250LWZhbWlseTonQml0c3RyZWFtIFZlcmEgU2Fucyc7dGV4dC1hbGlnbjpjZW50ZXI7dGV4dC1hbmNob3I6bWlkZGxlO2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Ik0gNDc0LjUwODg4LDcxOC4yMjg0MSBIIDMwMy40OTU0NyB2IC0yMi4zMDEzNCBjIC0yLjRlLTQsLTM3Ljk1MTA4IDQuMzAzNTIsLTY4Ljc2MjExIDEyLjkxMTMsLTkyLjQzMzE5IDguNjA3MjgsLTIzLjY3MDMyIDIzLjYzMzUyLC00NS4yODY5NSA0MC42NTMyNCwtNjQuODQ5OTYgMTcuMDE5MTQsLTE5LjU2MjExIDQxLjk4NzM0LC0yNi4zMzI2NCAxMDEuNDU3OTMsLTc1LjYzMDg1IDMxLjY5MDk1LC0yNS44MjIwMyA1NS4yODEzLC03Ny4xNTIzIDU1LjI4MTc1LC05OC42NzE3NCAyLjIxMjMyLC01Ni45MjI0NSAtMTMuOTM5ODMsLTc5LjM0MjIgLTM0LjU2Mjg3LC05OS45NjUyNCAtMjIuNjczNTUsLTE5LjY3NzE3IC02MC42NzAyNywtMzAuMDY5OTggLTkwLjk5ODkyLC0zMC4wNjk5OCAtMjcuNzc5MjEsNi45ZS00IC02OC40NjczNSw4LjA4ODcxIC04Ny43NjY2LDI1LjM3MDQ3IC0yNS45MzgxNywxNy4yODMwOCAtNjUuMjM3NDcsNzMuNzA2MTEgLTU3LjA0Njg3LDEzMC41NDU3NyBsIC0xOTQuNTE2OTQzLDEuNzAyMjIgYyAwLC0xNTcuMjEzOTkgMjkuMzkzNjk5LC0xOTguNjk0NjUgOTkuMDA0MTEzLC0yNjMuMDMwMzIgNjcuMzk3MzksLTU0LjM3NjY0MyAxMjYuNTMxMjgsLTczLjI2ODM2NSAyNDMuODQ3NTcsLTczLjI2ODM2NSA4OS43MTc5MSwwIDE2MS44OTcyOCwxNy44MDI4MSAyMTQuMzI1NTIsNTMuNDA1ODU1IDcxLjIwNzE0LDQ4LjEyNDcyIDEyMi4zMDEwNSwxMTEuMTgzNTQgMTIyLjMwMTA1LDIzMC4xMTI4MSAtNi45ZS00LDQ0LjMyMDgxIC0xOS4xNTI1Myw5MC43ODYzOCAtNDMuMDcyNiwxMjguMzMyOTkgLTE4LjM4OTQ3LDMwLjkwOTM4IC02MC4zNzUxMSw2Ni40NTIzNiAtMTE4LjIxMjM3LDEwNC40MTYyOCAtNDIuODM2MDcsMjUuNzY4NiAtNjYuNjcxOTYsNTMuMTE5MjYgLTc3LjAzOTY0LDcyLjA5NDYgLTEwLjM2ODYzLDE4Ljk3NjAzIC0xNS41NTI3MSw0My43MjI2NyAtMTUuNTUyMjUsNzQuMjM5OTkgeiIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgxMTQ3NCIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS4xMDYzODMsLTUuNTMxOTE0OSkiCiAgICAgICAgIGQ9Im0gNDgyLjM4Mjk4LDg2OS44MDkwMiBhIDk0LjA0MjU1Nyw3My4wMjEyNzggMCAxIDEgLTE4OC4wODUxMSwwIDk0LjA0MjU1Nyw3My4wMjEyNzggMCAxIDEgMTg4LjA4NTExLDAgeiIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=", + title: "Bookcase", + description: "A small, public cabinet with books. Anyone can leave or take a book", + minzoom: 12, + color: "#0000ff", + overpassTags: "amenity=public_bookcase", + presets: [ + { + // icon: optional. Uses the layer icon by default + // title: optional. Uses the layer title by default + // description: optional. Uses the layer description by default + // tags: optional list {k:string, v:string}[] + } + ], + tagRenderings: [ + { + // If this key is present, then... + key: "name", + // Use this string to render + render: "{name}", + // 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: "Wat is de naam van dit boekenruilkastje?", + // If a value is added with the textfield, this extra tag is addded. Optional field + addExtraTags: [{ + "k": "fixme", + "v": "Added with mapcomplete, to be checked" + }], + // 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: "noname=yes", + then: "Dit boekenruilkastje heeft geen naam" + } + ] + } + ] + } + + public static exampleLayout = { + name: "bookcases", + title: "Custom Open bookcases map", + description: "Welcome to a custom layout", + language: "en", + layers: [CustomLayoutFromJSON.exampleLayer], + startZoom: 12, + startLat: 0, + startLon: 0, + icon: "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcKICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIgogICB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciCiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgaWQ9InN2ZzExMzgyIgogICBoZWlnaHQ9IjkwMCIKICAgd2lkdGg9IjkwMCIKICAgdmVyc2lvbj0iMS4wIj4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGExMCI+CiAgICA8cmRmOlJERj4KICAgICAgPGNjOldvcmsKICAgICAgICAgcmRmOmFib3V0PSIiPgogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PgogICAgICAgIDxkYzp0eXBlCiAgICAgICAgICAgcmRmOnJlc291cmNlPSJodHRwOi8vcHVybC5vcmcvZGMvZGNtaXR5cGUvU3RpbGxJbWFnZSIgLz4KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4KICAgICAgPC9jYzpXb3JrPgogICAgPC9yZGY6UkRGPgogIDwvbWV0YWRhdGE+CiAgPGRlZnMKICAgICBpZD0iZGVmczExMzg0IiAvPgogIDxnCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjkwMTAzMjU4LDAsMCwwLjkwMTAzMjU4LDExMi44NDA1OCwtMS45MDYwMTc3KSI+CiAgICA8ZwogICAgICAgaWQ9ImcxMTQ3NiI+CiAgICAgIDxwYXRoCiAgICAgICAgIGlkPSJwYXRoMTE0NzIiCiAgICAgICAgIHN0eWxlPSJmb250LXN0eWxlOm5vcm1hbDtmb250LXdlaWdodDpub3JtYWw7Zm9udC1zaXplOjEyMDEuOTI0OTI2NzZweDtmb250LWZhbWlseTonQml0c3RyZWFtIFZlcmEgU2Fucyc7dGV4dC1hbGlnbjpjZW50ZXI7dGV4dC1hbmNob3I6bWlkZGxlO2ZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MXB4O3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1vcGFjaXR5OjEiCiAgICAgICAgIGQ9Ik0gNDc0LjUwODg4LDcxOC4yMjg0MSBIIDMwMy40OTU0NyB2IC0yMi4zMDEzNCBjIC0yLjRlLTQsLTM3Ljk1MTA4IDQuMzAzNTIsLTY4Ljc2MjExIDEyLjkxMTMsLTkyLjQzMzE5IDguNjA3MjgsLTIzLjY3MDMyIDIzLjYzMzUyLC00NS4yODY5NSA0MC42NTMyNCwtNjQuODQ5OTYgMTcuMDE5MTQsLTE5LjU2MjExIDQxLjk4NzM0LC0yNi4zMzI2NCAxMDEuNDU3OTMsLTc1LjYzMDg1IDMxLjY5MDk1LC0yNS44MjIwMyA1NS4yODEzLC03Ny4xNTIzIDU1LjI4MTc1LC05OC42NzE3NCAyLjIxMjMyLC01Ni45MjI0NSAtMTMuOTM5ODMsLTc5LjM0MjIgLTM0LjU2Mjg3LC05OS45NjUyNCAtMjIuNjczNTUsLTE5LjY3NzE3IC02MC42NzAyNywtMzAuMDY5OTggLTkwLjk5ODkyLC0zMC4wNjk5OCAtMjcuNzc5MjEsNi45ZS00IC02OC40NjczNSw4LjA4ODcxIC04Ny43NjY2LDI1LjM3MDQ3IC0yNS45MzgxNywxNy4yODMwOCAtNjUuMjM3NDcsNzMuNzA2MTEgLTU3LjA0Njg3LDEzMC41NDU3NyBsIC0xOTQuNTE2OTQzLDEuNzAyMjIgYyAwLC0xNTcuMjEzOTkgMjkuMzkzNjk5LC0xOTguNjk0NjUgOTkuMDA0MTEzLC0yNjMuMDMwMzIgNjcuMzk3MzksLTU0LjM3NjY0MyAxMjYuNTMxMjgsLTczLjI2ODM2NSAyNDMuODQ3NTcsLTczLjI2ODM2NSA4OS43MTc5MSwwIDE2MS44OTcyOCwxNy44MDI4MSAyMTQuMzI1NTIsNTMuNDA1ODU1IDcxLjIwNzE0LDQ4LjEyNDcyIDEyMi4zMDEwNSwxMTEuMTgzNTQgMTIyLjMwMTA1LDIzMC4xMTI4MSAtNi45ZS00LDQ0LjMyMDgxIC0xOS4xNTI1Myw5MC43ODYzOCAtNDMuMDcyNiwxMjguMzMyOTkgLTE4LjM4OTQ3LDMwLjkwOTM4IC02MC4zNzUxMSw2Ni40NTIzNiAtMTE4LjIxMjM3LDEwNC40MTYyOCAtNDIuODM2MDcsMjUuNzY4NiAtNjYuNjcxOTYsNTMuMTE5MjYgLTc3LjAzOTY0LDcyLjA5NDYgLTEwLjM2ODYzLDE4Ljk3NjAzIC0xNS41NTI3MSw0My43MjI2NyAtMTUuNTUyMjUsNzQuMjM5OTkgeiIgLz4KICAgICAgPHBhdGgKICAgICAgICAgaWQ9InBhdGgxMTQ3NCIKICAgICAgICAgc3R5bGU9ImZpbGw6IzAwMDAwMDtmaWxsLW9wYWNpdHk6MTtzdHJva2U6bm9uZTtzdHJva2Utd2lkdGg6MztzdHJva2UtbGluZWNhcDpzcXVhcmU7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1kYXNob2Zmc2V0OjA7c3Ryb2tlLW9wYWNpdHk6MSIKICAgICAgICAgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMS4xMDYzODMsLTUuNTMxOTE0OSkiCiAgICAgICAgIGQ9Im0gNDgyLjM4Mjk4LDg2OS44MDkwMiBhIDk0LjA0MjU1Nyw3My4wMjEyNzggMCAxIDEgLTE4OC4wODUxMSwwIDk0LjA0MjU1Nyw3My4wMjEyNzggMCAxIDEgMTg4LjA4NTExLDAgeiIgLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=" + } + + + public static FromQueryParam(layoutFromBase64: string): Layout { + if(layoutFromBase64 === "test"){ + console.log(btoa(JSON.stringify(CustomLayoutFromJSON.exampleLayout))); + return CustomLayoutFromJSON.LayoutFromJSON(CustomLayoutFromJSON.exampleLayout); + } + const spec = JSON.parse(atob(layoutFromBase64)); + return CustomLayoutFromJSON.LayoutFromJSON(spec); + } + + private static TagRenderingFromJson(json: any): TagRenderingOptions { + + if (typeof (json) === "string") { + return new FixedText(json); + } + + let freeform = undefined; + if (json.key !== undefined && json.render !== undefined) { + const type = json.type ?? "text"; + freeform = { + key: json.key, + template: json.render.replace("{" + json.key + "}", "$" + type + "$"), + renderTemplate: json.render, + extraTags: CustomLayoutFromJSON.TagsFromJson(json.addExtraTags), + } + } + + let mappings = undefined; + if (json.mappings !== undefined) { + mappings = []; + for (const mapping of json.mappings) { + mappings.push({ + k: new And(CustomLayoutFromJSON.TagsFromJson(mapping.if)), txt: mapping.then + }) + } + } + + return new TagRenderingOptions({ + question: json.question, + freeform: freeform, + mappings: mappings + }) + } + + private static PresetFromJson(layout: any, preset: any): Preset { + const t = CustomLayoutFromJSON.MaybeTranslation; + const tags = CustomLayoutFromJSON.TagsFromJson; + return { + icon: preset.icon ?? 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: any, styleJson: any): ((tags) => { + color: string, + weight?: number, + icon: { + iconUrl: string, + iconSize: number[], + }, + }) { + return (tags) => { + return { + color: layout.color, + weight: 10, + icon: { + iconUrl: layout.icon, + iconSize: [40, 40], + }, + } + }; + } + + private static TagFromJson(json: any): Tag { + if (json === undefined) { + return undefined; + } + if (typeof (json) === "string") { + const kv = json.split("="); + return new Tag(kv[0].trim(), kv[1].trim()); + } + return new Tag(json.k.trim(), json.v.trim()) + } + + private static TagsFromJson(json: any): Tag[] { + if (json === undefined) { + return undefined; + } + if (typeof (json) === "string") { + return json.split(",").map(CustomLayoutFromJSON.TagFromJson); + } + return json.map(CustomLayoutFromJSON.TagFromJson) + } + + private static LayerFromJson(json: any): LayerDefinition { + const t = CustomLayoutFromJSON.MaybeTranslation; + const tr = CustomLayoutFromJSON.TagRenderingFromJson; + return new LayerDefinition( + json.id, + { + description: t(json.description), + name: t(json.title), + icon: json.icon, + minzoom: 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(CustomLayoutFromJSON.TagsFromJson(json.overpassTags)), + wayHandling: LayerDefinition.WAYHANDLING_CENTER_AND_WAY, + maxAllowedOverlapPercentage: 0, + style: CustomLayoutFromJSON.StyleFromJson(json, json.style) + } + ) + } + + + private static MaybeTranslation(json: any): Translation | string { + if (json === undefined) { + return undefined; + } + if (typeof (json) === "string") { + return json; + } + return new Translation(json); + } + + private static LayoutFromJSON(json: any) { + const t = CustomLayoutFromJSON.MaybeTranslation; + const layout = new Layout(json.name, + [json.language], + t(json.title), + json.layers.map(CustomLayoutFromJSON.LayerFromJson), + json.startZoom, + json.startLat, + json.startLon, + new Combine(['

', t(json.title), '


', t(json.description)]) + ); + layout.icon = json.icon; + return layout; + } + + + public static TagRenderingOptionsFromJson(spec: any): TagRenderingOptions { + return new TagRenderingOptions(spec); + } + +} \ No newline at end of file diff --git a/Customizations/JSON/TagRenderingParser.ts b/Customizations/JSON/TagRenderingParser.ts deleted file mode 100644 index 7b3107f..0000000 --- a/Customizations/JSON/TagRenderingParser.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {TagRenderingOptions} from "../TagRenderingOptions"; -import {LayerDefinition} from "../LayerDefinition"; - - -export class CustomizationFromJSON { - - public exampleLayer = { - id: "bookcases", - - } - - /* - public static LayerFromJson(spec: any) : LayerDefinition{ - return new LayerDefinition(spec.id,{ - - }) - } - */ - public static TagRenderingOptionsFromJson(spec: any) : TagRenderingOptions{ - return new TagRenderingOptions(spec); - } - - -} \ No newline at end of file diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts index 55a5bbb..9c02c01 100644 --- a/Customizations/LayerDefinition.ts +++ b/Customizations/LayerDefinition.ts @@ -94,14 +94,9 @@ export class LayerDefinition { static WAYHANDLING_CENTER_AND_WAY = 2; constructor(id: string, options: { - name: string, + name: string | UIElement, description: string | UIElement, - presets: { - tags: Tag[], - title: string | UIElement, - description?: string | UIElement, - icon?: string - }[], + presets: Preset[], icon: string, minzoom: number, overpassFilter: TagsFilter, diff --git a/InitUiElements.ts b/InitUiElements.ts index 18a9c8b..090c7df 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -55,7 +55,7 @@ export class InitUiElements { } const tabs = [ - {header: ``, content: welcome}, + {header: Img.AsImageElement(layoutToUse.icon), content: welcome}, {header: ``, content: Translations.t.general.openStreetMapIntro}, ] diff --git a/UI/Img.ts b/UI/Img.ts index 23e399d..8a08a1b 100644 --- a/UI/Img.ts +++ b/UI/Img.ts @@ -1,4 +1,19 @@ export class Img { + + /** + * If the source is an svg element, it is returned as is. + * If not, the source is wrapped into a 'img'-tag + * @param source + * @constructor + */ + static AsImageElement(source: string): string{ + if(source.startsWith("`; + }else{ + return `` + } + } + static readonly checkmark = ``; static readonly no_checkmark = ``; diff --git a/assets/help.svg b/assets/help.svg index 092ffad..1f6bad7 100644 --- a/assets/help.svg +++ b/assets/help.svg @@ -1,20 +1,14 @@ - - + height="900" + width="900" + version="1.0"> @@ -27,45 +21,22 @@ - + id="layer1" + transform="matrix(0.90103258,0,0,0.90103258,112.84058,-1.9060177)"> + style="font-style:normal;font-weight:normal;font-size:1201.92492676px;font-family:'Bitstream Vera Sans';text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 474.50888,718.22841 H 303.49547 v -22.30134 c -2.4e-4,-37.95108 4.30352,-68.76211 12.9113,-92.43319 8.60728,-23.67032 23.63352,-45.28695 40.65324,-64.84996 17.01914,-19.56211 41.98734,-26.33264 101.45793,-75.63085 31.69095,-25.82203 55.2813,-77.1523 55.28175,-98.67174 2.21232,-56.92245 -13.93983,-79.3422 -34.56287,-99.96524 -22.67355,-19.67717 -60.67027,-30.06998 -90.99892,-30.06998 -27.77921,6.9e-4 -68.46735,8.08871 -87.7666,25.37047 -25.93817,17.28308 -65.23747,73.70611 -57.04687,130.54577 l -194.516943,1.70222 c 0,-157.21399 29.393699,-198.69465 99.004113,-263.03032 67.39739,-54.376643 126.53128,-73.268365 243.84757,-73.268365 89.71791,0 161.89728,17.80281 214.32552,53.405855 71.20714,48.12472 122.30105,111.18354 122.30105,230.11281 -6.9e-4,44.32081 -19.15253,90.78638 -43.0726,128.33299 -18.38947,30.90938 -60.37511,66.45236 -118.21237,104.41628 -42.83607,25.7686 -66.67196,53.11926 -77.03964,72.0946 -10.36863,18.97603 -15.55271,43.72267 -15.55225,74.23999 z" /> + style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + transform="translate(1.106383,-5.5319149)" + d="m 482.38298,869.80902 a 94.042557,73.021278 0 1 1 -188.08511,0 94.042557,73.021278 0 1 1 188.08511,0 z" /> diff --git a/clean.sh b/clean.sh index 5021175..08cf074 100755 --- a/clean.sh +++ b/clean.sh @@ -16,7 +16,7 @@ rm q*.html rm assets/generated/* for f in ./*.html; do - if [[ "$f" == "./index.html" ]] || [[ "$f" == "./land.html" ]] || [[ "$f" == "./test.html" ]] + if [[ "$f" == "./index.html" ]] || [[ "$f" == "./land.html" ]] || [[ "$f" == "./test.html" ]] || [[ "$f" == "./preferences.html" ]] || [[ "$f" == "./customGenerator.html" ]] then echo "Not removing $f" else diff --git a/index.css b/index.css index b0965fc..67c8822 100644 --- a/index.css +++ b/index.css @@ -462,6 +462,12 @@ form { margin: 0 10px 0 18px; } +#filter__selection ul svg { + width: 20px; + height: auto; + margin: 0 10px 0 18px; +} + .filter__label { font-size: 16px; transform: translateY(75px); @@ -1149,6 +1155,7 @@ form { padding: 0.5em; } + .tab-content { padding: 1em; z-index: 5002; diff --git a/index.ts b/index.ts index 69d1a1a..457423e 100644 --- a/index.ts +++ b/index.ts @@ -18,6 +18,7 @@ import {TagRenderingOptions} from "./Customizations/TagRenderingOptions"; import {TagRendering} from "./Customizations/TagRendering"; import {Img} from "./UI/Img"; import Combine from "./UI/Base/Combine"; +import {CustomLayoutFromJSON} from "./Customizations/JSON/CustomLayoutFromJSON"; // --------------------- Special actions based on the parameters ----------------- @@ -41,6 +42,7 @@ if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { // ----------------- SELECT THE RIGHT QUESTSET ----------------- let defaultLayout = "bookcases" +let hash = window.location.hash; const path = window.location.pathname.split("/").slice(-1)[0]; if (path !== "index.html") { @@ -64,7 +66,15 @@ for (const k in AllKnownLayouts.allSets) { defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout).data; -const layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout] ?? AllKnownLayouts["all"]; +let layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout] ?? AllKnownLayouts["all"]; + + +const layoutFromBase64 = QueryParameters.GetQueryParameter("userlayout", "false").data; +if(layoutFromBase64 === "true"){ + layoutToUse = CustomLayoutFromJSON.FromQueryParam(hash.substr(1)); +} + + if (layoutToUse === undefined) { console.log("Incorrect layout") new FixedUiElement("Error: incorrect layout " + defaultLayout + "
Go back").AttachTo("centermessage").onClick(() => { @@ -73,8 +83,8 @@ if (layoutToUse === undefined) { } console.log("Using layout: ", layoutToUse.name); -TagRendering.injectFunction(); +TagRendering.injectFunction(); State.state = new State(layoutToUse); InitUiElements.InitBaseMap(); diff --git a/preferences.html b/preferences.html new file mode 100644 index 0000000..c4aa00b --- /dev/null +++ b/preferences.html @@ -0,0 +1,27 @@ + + + + + Preferences editor + + + + + + +

Preferences editor - developers only

+Only use if you know what you're doing. To prevent newbies to make mistakes here, editing a mapcomplete-preference is only available if over 500 changes
+Editing any preference -including non-mapcomplete ones- is available when you have more then 2500 changesets. Until that point, only editing mapcomplete-preferences is possible. +
'maindiv' not attached
+ + + \ No newline at end of file