Refactored out 'layout.ts'

This commit is contained in:
Pieter Vander Vennet 2020-11-11 16:23:49 +01:00
parent 36f5e896df
commit 73f32e0ecf
30 changed files with 465 additions and 400 deletions

View file

@ -120,10 +120,27 @@ export default class AllTranslationAssets {
ph_open: new Translation( {"en":"opened","ca":"tancat","es":"abierto","nl":"open"} ), ph_open: new Translation( {"en":"opened","ca":"tancat","es":"abierto","nl":"open"} ),
}, },
}, },
favourite: { title: new Translation( {"en":"Personal theme","nl":"Persoonlijk thema","es":"Interficie personal","ca":"Interfície personal","gl":"Tema personalizado","de":"Persönliches Thema"} ), favourite: {
description: new Translation( {"en":"Create a personal theme based on all the available layers of all themes","es":"Crea una interficie basada en todas las capas disponibles de todas las interficies","ca":"Crea una interfície basada en totes les capes disponibles de totes les interfícies","gl":"Crea un tema baseado en todas as capas dispoñíbeis de todos os temas","de":"Erstellen Sie ein persönliches Thema auf der Grundlage aller verfügbaren Ebenen aller Themen"} ), panelIntro: new Translation({
panelIntro: new Translation( {"en":"<h3>Your personal theme</h3>Activate your favourite layers from all the official themes","ca":"<h3>La teva interfície personal</h3>Activa les teves capes favorites de totes les interfícies oficials","es":"<h3>Tu interficie personal</h3>Activa tus capas favoritas de todas las interficies oficiales","gl":"<h3>O teu tema personalizado</h3>Activa as túas capas favoritas de todos os temas oficiais","de":"<h3>Ihr persönliches Thema</h3>Aktivieren Sie Ihre Lieblingsebenen aus allen offiziellen Themen"} ), "en": "<h3>Your personal theme</h3>Activate your favourite layers from all the official themes",
loginNeeded: new Translation( {"en":"<h3>Log in</h3>A personal layout is only available for OpenStreetMap users","es":"<h3>Entrar</h3>El diseño personalizado sólo está disponible para los usuarios de OpenstreetMap","ca":"<h3>Entrar</h3>El disseny personalizat només està disponible pels usuaris d' OpenstreetMap","gl":"<h3>Iniciar a sesión</h3>O deseño personalizado só está dispoñíbel para os usuarios do OpenstreetMap","de":"<h3>Anmelden</h3>Ein persönliches Layout ist nur für OpenStreetMap-Benutzer verfügbar"} ), "ca": "<h3>La teva interfície personal</h3>Activa les teves capes favorites de totes les interfícies oficials",
reload: new Translation( {"en":"Reload the data","es":"Recargar datos","ca":"Recarregar dades","gl":"Recargar os datos","de":"Daten neu laden"} ), "es": "<h3>Tu interficie personal</h3>Activa tus capas favoritas de todas las interficies oficiales",
}, "gl": "<h3>O teu tema personalizado</h3>Activa as túas capas favoritas de todos os temas oficiais",
"de": "<h3>Ihr persönliches Thema</h3>Aktivieren Sie Ihre Lieblingsebenen aus allen offiziellen Themen"
}),
loginNeeded: new Translation({
"en": "<h3>Log in</h3>A personal layout is only available for OpenStreetMap users",
"es": "<h3>Entrar</h3>El diseño personalizado sólo está disponible para los usuarios de OpenstreetMap",
"ca": "<h3>Entrar</h3>El disseny personalizat només està disponible pels usuaris d' OpenstreetMap",
"gl": "<h3>Iniciar a sesión</h3>O deseño personalizado só está dispoñíbel para os usuarios do OpenstreetMap",
"de": "<h3>Anmelden</h3>Ein persönliches Layout ist nur für OpenStreetMap-Benutzer verfügbar"
}),
reload: new Translation({
"en": "Reload the data",
"es": "Recargar datos",
"ca": "Recarregar dades",
"gl": "Recargar os datos",
"de": "Daten neu laden"
}),
},
}} }}

View file

@ -1,4 +1,3 @@
import {Layout} from "./Layout";
import * as bookcases from "../assets/themes/bookcases/Bookcases.json"; import * as bookcases from "../assets/themes/bookcases/Bookcases.json";
import * as aed from "../assets/themes/aed/aed.json"; import * as aed from "../assets/themes/aed/aed.json";
import * as toilets from "../assets/themes/toilets/toilets.json"; import * as toilets from "../assets/themes/toilets/toilets.json";
@ -15,17 +14,18 @@ import * as fritures from "../assets/themes/fritures/fritures.json"
import * as benches from "../assets/themes/benches/benches.json"; import * as benches from "../assets/themes/benches/benches.json";
import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json" import * as charging_stations from "../assets/themes/charging_stations/charging_stations.json"
import * as widths from "../assets/themes/widths/width.json" import * as widths from "../assets/themes/widths/width.json"
import * as drinking_water from "../assets/themes/drinking_water/drinking_water.json"
import {PersonalLayout} from "../Logic/PersonalLayout";
import LayerConfig from "./JSON/LayerConfig"; import LayerConfig from "./JSON/LayerConfig";
import SharedLayers from "./SharedLayers"; import SharedLayers from "./SharedLayers";
import * as personal from "../assets/themes/personalLayout/personalLayout.json"
import LayoutConfig from "./JSON/LayoutConfig";
export class AllKnownLayouts { export class AllKnownLayouts {
public static allLayers: Map<string, LayerConfig> = undefined; public static allLayers: Map<string, LayerConfig> = undefined;
private static GenerateCycloFix(): Layout { private static GenerateCycloFix(): LayoutConfig {
const layout = Layout.LayoutFromJSON(cyclofix, SharedLayers.sharedLayers) const layout = new LayoutConfig(cyclofix)
const now = new Date(); const now = new Date();
const m = now.getMonth() + 1; const m = now.getMonth() + 1;
const day = new Date().getDate() + 1; const day = new Date().getDate() + 1;
@ -41,63 +41,31 @@ export class AllKnownLayouts {
return layout; return layout;
} }
public static layoutsList: LayoutConfig[] = [
private static GenerateWidths(): Layout { new LayoutConfig(personal),
const layout = Layout.LayoutFromJSON(widths, SharedLayers.sharedLayers);
layout.enableUserBadge = false;
layout.enableShareScreen = false;
layout.enableMoreQuests = false;
layout.enableLayers = false;
layout.hideFromOverview = true;
layout.enableSearch = false;
layout.enableGeolocation = false;
return layout;
}
private static GenerateBuurtNatuur(): Layout {
const layout = Layout.LayoutFromJSON(buurtnatuur, SharedLayers.sharedLayers);
layout.enableMoreQuests = false;
layout.enableShareScreen = false;
layout.hideFromOverview = true;
console.log("Buurtnatuur:",layout)
return layout;
}
private static GenerateBikeMonitoringStations(): Layout {
const layout = Layout.LayoutFromJSON(bike_monitoring_stations, SharedLayers.sharedLayers);
layout.hideFromOverview = true;
return layout;
}
public static layoutsList: Layout[] = [
new PersonalLayout(),
Layout.LayoutFromJSON(shops, SharedLayers.sharedLayers),
Layout.LayoutFromJSON(bookcases, SharedLayers.sharedLayers),
Layout.LayoutFromJSON(aed, SharedLayers.sharedLayers),
Layout.LayoutFromJSON(toilets, SharedLayers.sharedLayers),
Layout.LayoutFromJSON(artworks, SharedLayers.sharedLayers),
AllKnownLayouts.GenerateCycloFix(), AllKnownLayouts.GenerateCycloFix(),
Layout.LayoutFromJSON(ghostbikes, SharedLayers.sharedLayers), new LayoutConfig(aed),
Layout.LayoutFromJSON(nature, SharedLayers.sharedLayers), new LayoutConfig(bookcases),
Layout.LayoutFromJSON(cyclestreets, SharedLayers.sharedLayers), new LayoutConfig(toilets),
Layout.LayoutFromJSON(maps, SharedLayers.sharedLayers), new LayoutConfig(artworks),
Layout.LayoutFromJSON(fritures, SharedLayers.sharedLayers), new LayoutConfig(ghostbikes),
Layout.LayoutFromJSON(benches, SharedLayers.sharedLayers), new LayoutConfig(shops),
Layout.LayoutFromJSON(charging_stations, SharedLayers.sharedLayers), new LayoutConfig(drinking_water),
AllKnownLayouts.GenerateWidths(), new LayoutConfig(nature),
AllKnownLayouts.GenerateBuurtNatuur(), new LayoutConfig(cyclestreets),
AllKnownLayouts.GenerateBikeMonitoringStations(), new LayoutConfig(maps),
new LayoutConfig(fritures),
new LayoutConfig(benches),
new LayoutConfig(charging_stations),
new LayoutConfig(widths),
new LayoutConfig(buurtnatuur),
new LayoutConfig(bike_monitoring_stations),
]; ];
public static allSets: Map<string, Layout> = AllKnownLayouts.AllLayouts(); public static allSets: Map<string, LayoutConfig> = AllKnownLayouts.AllLayouts();
private static AllLayouts(): Map<string, Layout> { private static AllLayouts(): Map<string, LayoutConfig> {
this.allLayers = new Map<string, LayerConfig>(); this.allLayers = new Map<string, LayerConfig>();
for (const layout of this.layoutsList) { for (const layout of this.layoutsList) {
for (let i = 0; i < layout.layers.length; i++) { for (let i = 0; i < layout.layers.length; i++) {
@ -118,7 +86,7 @@ export class AllKnownLayouts {
} }
} }
const allSets: Map<string, Layout> = new Map(); const allSets: Map<string, LayoutConfig> = new Map();
for (const layout of this.layoutsList) { for (const layout of this.layoutsList) {
allSets[layout.id] = layout; allSets[layout.id] = layout;
allSets[layout.id.toLowerCase()] = layout; allSets[layout.id.toLowerCase()] = layout;

View file

@ -2,35 +2,9 @@ import {AndOrTagConfigJson} from "./TagConfigJson";
import {And, Or, RegexTag, Tag, TagsFilter} from "../../Logic/Tags"; import {And, Or, RegexTag, Tag, TagsFilter} from "../../Logic/Tags";
import {Utils} from "../../Utils"; import {Utils} from "../../Utils";
import {Translation} from "../../UI/i18n/Translation";
export class FromJSON { export class FromJSON {
public static Translation(json: string | any): Translation {
if (json === undefined) {
return undefined;
}
if (typeof (json) === "string") {
return new Translation({"*": json});
}
if(json.render !== undefined){
console.error("Using a 'render' where a translation is expected. Content is", json.render);
throw "ERROR: using a 'render' where none is expected"
}
const tr = {};
let keyCount = 0;
for (let key in json) {
keyCount++;
tr[key] = json[key]; // I'm doing this wrong, I know
}
if(keyCount == 0){
return undefined;
}
const transl = new Translation(tr);
return transl;
}
public static SimpleTag(json: string): Tag { public static SimpleTag(json: string): Tag {
const tag = Utils.SplitFirst(json, "="); const tag = Utils.SplitFirst(json, "=");
return new Tag(tag[0], tag[1]); return new Tag(tag[0], tag[1]);

View file

@ -0,0 +1,98 @@
import {Translation} from "../../UI/i18n/Translation";
import TagRenderingConfig from "./TagRenderingConfig";
import LayerConfig from "./LayerConfig";
import {LayoutConfigJson} from "./LayoutConfigJson";
import SharedLayers from "../SharedLayers";
import SharedTagRenderings from "../SharedTagRenderings";
export default class LayoutConfig {
public readonly id: string;
public readonly maintainer: string;
public readonly changesetmessage?: string;
public readonly version: string;
public readonly language: string[];
public readonly title: Translation;
public readonly shortDescription?: Translation;
public readonly description: Translation;
public readonly descriptionTail?: Translation;
public readonly icon: string;
public readonly socialImage?: string;
public readonly startZoom: number;
public readonly startLat: number;
public readonly startLon: number;
public readonly widenFactor: number;
public readonly roamingRenderings: TagRenderingConfig[];
public readonly defaultBackgroundId?: string;
public readonly layers: LayerConfig[];
public readonly hideFromOverview: boolean;
public readonly enableUserBadge: boolean;
public readonly enableShareScreen: boolean;
public readonly enableMoreQuests: boolean;
public readonly enableAddNewPoints: boolean;
public readonly enableLayers: boolean;
public readonly enableSearch: boolean;
public readonly enableGeolocation: boolean;
public readonly enableBackgroundLayerSelection: boolean;
public readonly customCss?: string;
constructor(json: LayoutConfigJson, context?:string) {
this.id = json.id;
context = (context ?? "")+"."+this.id;
this.maintainer = json.maintainer;
this.changesetmessage = json.changesetmessage;
this.version = json.version;
this.language = [];
if (typeof json.language === "string") {
this.language = [json.language];
} else {
this.language = json.language;
}
if(json.title === undefined){
throw "Title not defined in "+this.id;
}
if(json.description === undefined){
throw "Description not defined in "+this.id;
}
this.title = new Translation(json.title, context+".title");
this.description = new Translation(json.description, context+".description");
this.shortDescription = json.shortDescription === undefined ? this.description.FirstSentence() : new Translation(json.shortDescription, context+".shortdescription");
this.descriptionTail = json.descriptionTail === undefined ? new Translation({"*":""}, context) : new Translation(json.descriptionTail, context+".descriptionTail");
this.icon = json.icon;
this.socialImage = json.socialImage;
this.startZoom = json.startZoom;
this.startLat = json.startLat;
this.startLon = json.startLon;
this.widenFactor = json.widenFactor ?? 0.05;
this.roamingRenderings = (json.roamingRenderings ?? []).map((tr, i) => {
if (typeof tr === "string") {
if (SharedTagRenderings.SharedTagRendering[tr] !== undefined) {
return SharedTagRenderings.SharedTagRendering[tr];
}
}
return new TagRenderingConfig(tr, `${this.id}.roaming_renderings[${i}]`);
}
);
this.defaultBackgroundId = json.defaultBackgroundId;
this.layers = json.layers.map((layer, i) => {
if (typeof layer === "string")
if (SharedLayers.sharedLayers[layer] !== undefined) {
return SharedLayers.sharedLayers[layer];
} else {
throw "Unkown fixed layer " + layer;
}
return new LayerConfig(layer, `${this.id}.layers[${i}]`);
});
this.hideFromOverview = json.hideFromOverview ?? false;
this.enableUserBadge = json.enableUserBadge ?? true;
this.enableShareScreen = json.enableShareScreen ?? true;
this.enableMoreQuests = json.enableMoreQuests ?? true;
this.enableLayers = json.enableLayers ?? true;
this.enableSearch = json.enableSearch ?? true;
this.enableGeolocation = json.enableGeolocation ?? true;
this.enableAddNewPoints = json.enableAddNewPoints ?? true;
this.enableBackgroundLayerSelection = json.enableBackgroundLayerSelection ?? true;
this.customCss = json.customCss;
}
}

View file

@ -106,9 +106,21 @@ export interface LayoutConfigJson {
*/ */
layers: (LayerConfigJson | string)[], layers: (LayerConfigJson | string)[],
/**
* The URL of a custom CSS stylesheet to modify the layout
*/
customCss?: string;
/**
* If set to true, this layout will not be shown in the overview with more themes
*/
hideFromOverview?: boolean;
enableUserBadge?: boolean;
enableShareScreen?: boolean;
enableMoreQuests?: boolean;
enableLayers?: boolean;
enableSearch?: boolean;
enableAddNewPoints?: boolean;
enableGeolocation?: boolean;
enableBackgroundLayerSelection?: boolean;
} }

View file

@ -1,131 +0,0 @@
import {UIElement} from "../UI/UIElement";
import Translations from "../UI/i18n/Translations";
import Combine from "../UI/Base/Combine";
import State from "../State";
import LayerConfig from "./JSON/LayerConfig";
import {LayoutConfigJson} from "./JSON/LayoutConfigJson";
import TagRenderingConfig from "./JSON/TagRenderingConfig";
import {FromJSON} from "./JSON/FromJSON";
import {Translation} from "../UI/i18n/Translation";
import Svg from "../Svg";
import {Img} from "../UI/Img";
/**
* A layout is a collection of settings of the global view (thus: welcome text, title, selection of layers).
*/
export class Layout {
public id: string;
public icon: string = Img.AsData(Svg.bug);
public title: UIElement;
public maintainer: string;
public version: string;
public description: string | UIElement;
public changesetMessage: string;
public socialImage: string = "";
/**
* Custom CSS link
*/
public customCss: string = undefined;
public layers: LayerConfig[];
public welcomeMessage: UIElement;
public gettingStartedPlzLogin: UIElement;
public welcomeBackMessage: UIElement;
public welcomeTail: UIElement;
public supportedLanguages: string[];
public startzoom: number;
public startLon: number;
public startLat: number;
public enableAdd: boolean = true;
public enableUserBadge: boolean = true;
public enableSearch: boolean = true;
public enableLayers: boolean = true;
public enableBackgroundLayers: boolean = true;
public enableMoreQuests: boolean = true;
public enableShareScreen: boolean = true;
public enableGeolocation: boolean = true;
public hideFromOverview: boolean = false;
/**
* The BBOX of the currently visible map are widened by this factor, in order to make some panning possible.
* This number influences this
*/
public widenFactor: number = 0.07;
public defaultBackground: string = "osm";
public static LayoutFromJSON(json: LayoutConfigJson, sharedLayers): Layout {
const tr = FromJSON.Translation;
const layers = json.layers.map(jsonLayer => {
if(typeof jsonLayer === "string"){
return sharedLayers[jsonLayer];
}
return new LayerConfig(jsonLayer, "theme."+json.id);
});
const roaming: TagRenderingConfig[] = json.roamingRenderings?.map((tr, i) =>
new TagRenderingConfig(tr, `theme.${json.id}.roamingRendering[${i}]`)) ?? [];
for (const layer of layers) {
layer.tagRenderings.push(...roaming);
}
const layout = new Layout(
json.id,
typeof (json.language) === "string" ? [json.language] : json.language,
tr(json.title ?? "Title not defined"),
layers,
json.startZoom,
json.startLat,
json.startLon,
new Combine(["<h3>", tr(json.title), "</h3>", tr(json.description)]),
undefined,
undefined,
tr(json.descriptionTail)
);
layout.defaultBackground = json.defaultBackgroundId ?? "osm";
layout.widenFactor = json.widenFactor ?? 0.07;
layout.icon = json.icon;
layout.maintainer = json.maintainer;
layout.version = json.version;
layout.socialImage = json.socialImage;
layout.description = tr(json.shortDescription) ?? tr(json.description)?.FirstSentence();
layout.changesetMessage = json.changesetmessage;
return layout;
}
constructor(
id: string,
supportedLanguages: string[],
title: Translation | string,
layers: LayerConfig[],
startzoom: number,
startLat: number,
startLon: number,
welcomeMessage: UIElement | string,
gettingStartedPlzLogin: UIElement | string = new Combine([
Translations.t.general.getStartedLogin
.SetClass("soft")
.onClick(() => {State.state.osmConnection.AttemptLogin()}),
Translations.t.general.getStartedNewAccount
]),
welcomeBackMessage: UIElement | string = Translations.t.general.welcomeBack,
welcomeTail: UIElement | string = "",
) {
this.supportedLanguages = supportedLanguages;
this.title = Translations.WT(title)
this.startLon = startLon;
this.startLat = startLat;
this.startzoom = startzoom;
this.id = id;
this.layers = layers;
this.welcomeMessage = Translations.W(welcomeMessage)
this.gettingStartedPlzLogin = Translations.W(gettingStartedPlzLogin);
this.welcomeBackMessage = Translations.W(welcomeBackMessage);
this.welcomeTail = Translations.W(welcomeTail);
}
}

View file

@ -15,7 +15,6 @@ import {VariableUiElement} from "./UI/Base/VariableUIElement";
import {UpdateFromOverpass} from "./Logic/UpdateFromOverpass"; import {UpdateFromOverpass} from "./Logic/UpdateFromOverpass";
import {UIEventSource} from "./Logic/UIEventSource"; import {UIEventSource} from "./Logic/UIEventSource";
import {QueryParameters} from "./Logic/Web/QueryParameters"; import {QueryParameters} from "./Logic/Web/QueryParameters";
import {PersonalLayout} from "./Logic/PersonalLayout";
import {PersonalLayersPanel} from "./Logic/PersonalLayersPanel"; import {PersonalLayersPanel} from "./Logic/PersonalLayersPanel";
import Locale from "./UI/i18n/Locale"; import Locale from "./UI/i18n/Locale";
import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler"; import {StrayClickHandler} from "./Logic/Leaflet/StrayClickHandler";
@ -27,15 +26,15 @@ import {UserBadge} from "./UI/UserBadge";
import {SearchAndGo} from "./UI/SearchAndGo"; import {SearchAndGo} from "./UI/SearchAndGo";
import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler"; import {FullScreenMessageBox} from "./UI/FullScreenMessageBoxHandler";
import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler"; import {GeoLocationHandler} from "./Logic/Leaflet/GeoLocationHandler";
import {Layout} from "./Customizations/Layout";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {Utils} from "./Utils"; import {Utils} from "./Utils";
import BackgroundSelector from "./UI/BackgroundSelector"; import BackgroundSelector from "./UI/BackgroundSelector";
import AvailableBaseLayers from "./Logic/AvailableBaseLayers"; import AvailableBaseLayers from "./Logic/AvailableBaseLayers";
import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox"; import {FeatureInfoBox} from "./UI/Popup/FeatureInfoBox";
import SharedLayers from "./Customizations/SharedLayers";
import Svg from "./Svg"; import Svg from "./Svg";
import Link from "./UI/Base/Link"; import Link from "./UI/Base/Link";
import * as personal from "./assets/themes/personalLayout/personalLayout.json"
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
export class InitUiElements { export class InitUiElements {
@ -74,7 +73,7 @@ export class InitUiElements {
} }
static InitAll(layoutToUse: Layout, layoutFromBase64: string, testing: UIEventSource<string>, layoutName: string, static InitAll(layoutToUse: LayoutConfig, layoutFromBase64: string, testing: UIEventSource<string>, layoutName: string,
layoutDefinition: string = "") { layoutDefinition: string = "") {
if (layoutToUse === undefined) { if (layoutToUse === undefined) {
console.log("Incorrect layout") console.log("Incorrect layout")
@ -115,7 +114,7 @@ export class InitUiElements {
function updateFavs() { function updateFavs() {
const favs = State.state.favouriteLayers.data ?? []; const favs = State.state.favouriteLayers.data ?? [];
layoutToUse.layers = []; layoutToUse.layers.splice(0, layoutToUse.layers.length);
for (const fav of favs) { for (const fav of favs) {
const layer = AllKnownLayouts.allLayers[fav]; const layer = AllKnownLayouts.allLayers[fav];
if (!!layer) { if (!!layer) {
@ -140,8 +139,7 @@ export class InitUiElements {
} }
if (layoutToUse === AllKnownLayouts.allSets[PersonalLayout.NAME]) { if (layoutToUse.id === personal.id) {
State.state.favouriteLayers.addCallback(updateFavs); State.state.favouriteLayers.addCallback(updateFavs);
State.state.installedThemes.addCallback(updateFavs); State.state.installedThemes.addCallback(updateFavs);
} }
@ -208,15 +206,8 @@ export class InitUiElements {
.SetStyle(`position:relative;display:block;border: solid 2px #0005;cursor: pointer; z-index: 999; /*Just below leaflets zoom*/background-color: white;border-radius: 5px;width: 43px;height: 43px;`) .SetStyle(`position:relative;display:block;border: solid 2px #0005;cursor: pointer; z-index: 999; /*Just below leaflets zoom*/background-color: white;border-radius: 5px;width: 43px;height: 43px;`)
.AttachTo("geolocate-button"); .AttachTo("geolocate-button");
State.state.locationControl.ping(); State.state.locationControl.ping();
} }
public static FromBase64(layoutFromBase64: string): Layout {
return Layout.LayoutFromJSON(JSON.parse(atob(layoutFromBase64)), SharedLayers.sharedLayers);
}
static LoadLayoutFromHash(userLayoutParam: UIEventSource<string>) { static LoadLayoutFromHash(userLayoutParam: UIEventSource<string>) {
try { try {
let hash = location.hash.substr(1); let hash = location.hash.substr(1);
@ -236,7 +227,7 @@ export class InitUiElements {
hashFromLocalStorage.setData(hash); hashFromLocalStorage.setData(hash);
dedicatedHashFromLocalStorage.setData(hash); dedicatedHashFromLocalStorage.setData(hash);
} }
const layoutToUse = InitUiElements.FromBase64(hash); const layoutToUse = new LayoutConfig(JSON.parse(atob(hash)));
userLayoutParam.setData(layoutToUse.id); userLayoutParam.setData(layoutToUse.id);
return layoutToUse; return layoutToUse;
} catch (e) { } catch (e) {
@ -264,7 +255,7 @@ export class InitUiElements {
const layoutToUse = State.state.layoutToUse.data; const layoutToUse = State.state.layoutToUse.data;
let welcome: UIElement = new WelcomeMessage(); let welcome: UIElement = new WelcomeMessage();
if (layoutToUse.id === PersonalLayout.NAME) { if (layoutToUse.id === personal.id) {
welcome = new PersonalLayersPanel(); welcome = new PersonalLayersPanel();
} }
@ -351,7 +342,7 @@ export class InitUiElements {
let layerControlPanel: UIElement = undefined; let layerControlPanel: UIElement = undefined;
if (State.state.layoutToUse.data.enableBackgroundLayers) { if (State.state.layoutToUse.data.enableBackgroundLayerSelection) {
layerControlPanel = new BackgroundSelector(); layerControlPanel = new BackgroundSelector();
layerControlPanel.SetStyle("margin:1em"); layerControlPanel.SetStyle("margin:1em");
layerControlPanel.onClick(() => { }); layerControlPanel.onClick(() => { });
@ -451,7 +442,7 @@ export class InitUiElements {
State.state.layerUpdater = new UpdateFromOverpass(State.state); State.state.layerUpdater = new UpdateFromOverpass(State.state);
State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers; State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers;
const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackground); const queryParam = QueryParameters.GetQueryParameter("background", State.state.layoutToUse.data.defaultBackgroundId);
queryParam.addCallbackAndRun((selectedId: string) => { queryParam.addCallbackAndRun((selectedId: string) => {
const available = State.state.availableBackgroundLayers.data; const available = State.state.availableBackgroundLayers.data;

View file

@ -246,7 +246,12 @@ export default class MetaTagging {
static addMetatags(features: any[]) { static addMetatags(features: any[]) {
for (const metatag of MetaTagging.metatags) { for (const metatag of MetaTagging.metatags) {
metatag.addMetaTags(features); try {
metatag.addMetaTags(features);
} catch (e) {
console.error("Could not calculate metatag ", metatag.keys.join(","), ":", e)
}
} }
} }

View file

@ -1,9 +1,9 @@
import {OsmConnection, UserDetails} from "./OsmConnection"; import {OsmConnection, UserDetails} from "./OsmConnection";
import {UIEventSource} from "../UIEventSource"; import {UIEventSource} from "../UIEventSource";
import {ElementStorage} from "../ElementStorage"; import {ElementStorage} from "../ElementStorage";
import {Layout} from "../../Customizations/Layout";
import State from "../../State"; import State from "../../State";
import Locale from "../../UI/i18n/Locale"; import Locale from "../../UI/i18n/Locale";
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
export class ChangesetHandler { export class ChangesetHandler {
@ -26,7 +26,7 @@ export class ChangesetHandler {
public UploadChangeset( public UploadChangeset(
layout: Layout, layout: LayoutConfig,
allElements: ElementStorage, allElements: ElementStorage,
generateChangeXML: (csid: string) => string, generateChangeXML: (csid: string) => string,
continuation: () => void) { continuation: () => void) {
@ -85,10 +85,10 @@ export class ChangesetHandler {
private OpenChangeset( private OpenChangeset(
layout : Layout, layout : LayoutConfig,
continuation: (changesetId: string) => void) { continuation: (changesetId: string) => void) {
const commentExtra = layout.changesetMessage !== undefined ? " - " + layout.changesetMessage : ""; const commentExtra = layout.changesetmessage !== undefined ? " - " + layout.changesetmessage : "";
let surveySource = ""; let surveySource = "";
if (State.state.currentGPSLocation.data !== undefined) { if (State.state.currentGPSLocation.data !== undefined) {

View file

@ -3,10 +3,10 @@ import osmAuth from "osm-auth";
import {UIEventSource} from "../UIEventSource"; import {UIEventSource} from "../UIEventSource";
import {OsmPreferences} from "./OsmPreferences"; import {OsmPreferences} from "./OsmPreferences";
import {ChangesetHandler} from "./ChangesetHandler"; import {ChangesetHandler} from "./ChangesetHandler";
import {Layout} from "../../Customizations/Layout";
import {ElementStorage} from "../ElementStorage"; import {ElementStorage} from "../ElementStorage";
import {Img} from "../../UI/Img"; import {Img} from "../../UI/Img";
import Svg from "../../Svg"; import Svg from "../../Svg";
import LayoutConfig from "../../Customizations/JSON/LayoutConfig";
export class UserDetails { export class UserDetails {
@ -97,7 +97,7 @@ export class OsmConnection {
public UploadChangeset( public UploadChangeset(
layout: Layout, layout: LayoutConfig,
allElements: ElementStorage, allElements: ElementStorage,
generateChangeXML: (csid: string) => string, generateChangeXML: (csid: string) => string,
continuation: () => void = () => {}) { continuation: () => void = () => {}) {

View file

@ -5,12 +5,12 @@ import {UIEventSource} from "./UIEventSource";
import {AllKnownLayouts} from "../Customizations/AllKnownLayouts"; import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
import Combine from "../UI/Base/Combine"; import Combine from "../UI/Base/Combine";
import CheckBox from "../UI/Input/CheckBox"; import CheckBox from "../UI/Input/CheckBox";
import {PersonalLayout} from "./PersonalLayout"; import * as personal from "../assets/themes/personalLayout/personalLayout.json";
import {Layout} from "../Customizations/Layout";
import {SubtleButton} from "../UI/Base/SubtleButton"; import {SubtleButton} from "../UI/Base/SubtleButton";
import {FixedUiElement} from "../UI/Base/FixedUiElement"; import {FixedUiElement} from "../UI/Base/FixedUiElement";
import {Img} from "../UI/Img"; import {Img} from "../UI/Img";
import Svg from "../Svg"; import Svg from "../Svg";
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
export class PersonalLayersPanel extends UIElement { export class PersonalLayersPanel extends UIElement {
private checkboxes: UIElement[] = []; private checkboxes: UIElement[] = [];
@ -22,19 +22,19 @@ export class PersonalLayersPanel extends UIElement {
this.UpdateView([]); this.UpdateView([]);
const self = this; const self = this;
State.state.installedThemes.addCallback(extraThemes => { State.state.installedThemes.addCallback(extraThemes => {
self.UpdateView(extraThemes.map(layout => layout.layout)); self.UpdateView(extraThemes.map(layout => layout.layout.layoutConfig));
self.Update(); self.Update();
}) })
} }
private UpdateView(extraThemes: Layout[]) { private UpdateView(extraThemes: LayoutConfig[]) {
this.checkboxes = []; this.checkboxes = [];
const favs = State.state.favouriteLayers.data ?? []; const favs = State.state.favouriteLayers.data ?? [];
const controls = new Map<string, UIEventSource<boolean>>(); const controls = new Map<string, UIEventSource<boolean>>();
const allLayouts = AllKnownLayouts.layoutsList.concat(extraThemes); const allLayouts = AllKnownLayouts.layoutsList.concat(extraThemes);
for (const layout of allLayouts) { for (const layout of allLayouts) {
if (layout.id === PersonalLayout.NAME) { if (layout.id === personal.id) {
continue; continue;
} }
@ -44,11 +44,15 @@ export class PersonalLayersPanel extends UIElement {
"<b>", "<b>",
layout.title, layout.title,
"</b><br/>", "</b><br/>",
layout.description ?? "" layout.shortDescription ?? ""
]).SetStyle("background: #eee; display: block; padding: 0.5em; border-radius:0.5em; overflow:auto;") ]).SetStyle("background: #eee; display: block; padding: 0.5em; border-radius:0.5em; overflow:auto;")
this.checkboxes.push(header); this.checkboxes.push(header);
for (const layer of layout.layers) { for (const layer of layout.layers) {
if(layer === undefined){
console.warn("Undefined layer for ",layout.id)
continue;
}
if (typeof layer === "string") { if (typeof layer === "string") {
continue; continue;
} }

View file

@ -1,26 +0,0 @@
import {Layout} from "../Customizations/Layout";
import Translations from "../UI/i18n/Translations";
import {Img} from "../UI/Img";
import Svg from "../Svg";
export class PersonalLayout extends Layout {
public static NAME: string = "personal";
constructor() {
super(
PersonalLayout.NAME,
["en"],
Translations.t.favourite.title,
[],
12,
0,
0,
Translations.t.favourite.description,
);
this.maintainer = "MapComplete"
this.description = "The personal theme allows to select one or more layers from all the layouts, creating a truly personal editor"
this.icon = Img.AsData(Svg.add)
}
}

View file

@ -1,5 +1,4 @@
import {UIElement} from "./UI/UIElement"; import {UIElement} from "./UI/UIElement";
import {Layout} from "./Customizations/Layout";
import {Utils} from "./Utils"; import {Utils} from "./Utils";
import {ElementStorage} from "./Logic/ElementStorage"; import {ElementStorage} from "./Logic/ElementStorage";
import {Changes} from "./Logic/Osm/Changes"; import {Changes} from "./Logic/Osm/Changes";
@ -12,6 +11,7 @@ import {UIEventSource} from "./Logic/UIEventSource";
import {LocalStorageSource} from "./Logic/Web/LocalStorageSource"; import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {QueryParameters} from "./Logic/Web/QueryParameters"; import {QueryParameters} from "./Logic/Web/QueryParameters";
import {BaseLayer} from "./Logic/BaseLayer"; import {BaseLayer} from "./Logic/BaseLayer";
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
/** /**
* Contains the global state: a bunch of UI-event sources * Contains the global state: a bunch of UI-event sources
@ -40,7 +40,7 @@ export default class State {
public static runningFromConsole: boolean = false; public static runningFromConsole: boolean = false;
public readonly layoutToUse = new UIEventSource<Layout>(undefined); public readonly layoutToUse = new UIEventSource<LayoutConfig>(undefined);
/** /**
The mapping from id -> UIEventSource<properties> The mapping from id -> UIEventSource<properties>
@ -113,7 +113,7 @@ export default class State {
accuracy: number accuracy: number
}> = new UIEventSource<{ latlng: {lat:number, lng:number}, accuracy: number }>(undefined); }> = new UIEventSource<{ latlng: {lat:number, lng:number}, accuracy: number }>(undefined);
public layoutDefinition: string; public layoutDefinition: string;
public installedThemes: UIEventSource<{ layout: Layout; definition: string }[]>; public installedThemes: UIEventSource<{ layout: LayoutConfig; definition: string }[]>;
public layerControlIsOpened: UIEventSource<boolean> = QueryParameters.GetQueryParameter("layer-control-toggle", "false") public layerControlIsOpened: UIEventSource<boolean> = QueryParameters.GetQueryParameter("layer-control-toggle", "false")
.map<boolean>((str) => str !== "false", [], b => "" + b) .map<boolean>((str) => str !== "false", [], b => "" + b)
@ -122,7 +122,7 @@ export default class State {
str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n str => isNaN(Number(str)) ? 0 : Number(str), [], n => "" + n
); );
constructor(layoutToUse: Layout) { constructor(layoutToUse: LayoutConfig) {
const self = this; const self = this;
this.layoutToUse.setData(layoutToUse); this.layoutToUse.setData(layoutToUse);
@ -138,7 +138,7 @@ export default class State {
}) })
} }
this.zoom = asFloat( this.zoom = asFloat(
QueryParameters.GetQueryParameter("z", "" + layoutToUse.startzoom) QueryParameters.GetQueryParameter("z", "" + layoutToUse.startZoom)
.syncWith(LocalStorageSource.Get("zoom"))); .syncWith(LocalStorageSource.Get("zoom")));
this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat) this.lat = asFloat(QueryParameters.GetQueryParameter("lat", "" + layoutToUse.startLat)
.syncWith(LocalStorageSource.Get("lat"))); .syncWith(LocalStorageSource.Get("lat")));
@ -158,14 +158,14 @@ export default class State {
this.layoutToUse.addCallback(layoutToUse => { this.layoutToUse.addCallback(layoutToUse => {
const lcd = self.locationControl.data; const lcd = self.locationControl.data;
lcd.zoom = lcd.zoom ?? layoutToUse?.startzoom; lcd.zoom = lcd.zoom ?? layoutToUse?.startZoom;
lcd.lat = lcd.lat ?? layoutToUse?.startLat; lcd.lat = lcd.lat ?? layoutToUse?.startLat;
lcd.lon = lcd.lon ?? layoutToUse?.startLon; lcd.lon = lcd.lon ?? layoutToUse?.startLon;
self.locationControl.ping(); self.locationControl.ping();
}); });
function featSw(key: string, deflt: (layout: Layout) => boolean): UIEventSource<boolean> { function featSw(key: string, deflt: (layout: LayoutConfig) => boolean): UIEventSource<boolean> {
const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined); const queryParameterSource = QueryParameters.GetQueryParameter(key, undefined);
// I'm so sorry about someone trying to decipher this // I'm so sorry about someone trying to decipher this
@ -182,7 +182,7 @@ export default class State {
this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true); this.featureSwitchUserbadge = featSw("fs-userbadge", (layoutToUse) => layoutToUse?.enableUserBadge ?? true);
this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true); this.featureSwitchSearch = featSw("fs-search", (layoutToUse) => layoutToUse?.enableSearch ?? true);
this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true); this.featureSwitchLayers = featSw("fs-layers", (layoutToUse) => layoutToUse?.enableLayers ?? true);
this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAdd ?? true); this.featureSwitchAddNew = featSw("fs-add-new", (layoutToUse) => layoutToUse?.enableAddNewPoints ?? true);
this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true); this.featureSwitchWelcomeMessage = featSw("fs-welcome-message", () => true);
this.featureSwitchIframe = featSw("fs-iframe", () => false); this.featureSwitchIframe = featSw("fs-iframe", () => false);
this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true); this.featureSwitchMoreQuests = featSw("fs-more-quests", (layoutToUse) => layoutToUse?.enableMoreQuests ?? true);
@ -198,8 +198,8 @@ export default class State {
); );
this.installedThemes = this.osmConnection.preferencesHandler.preferences.map<{ layout: Layout, definition: string }[]>(allPreferences => { this.installedThemes = this.osmConnection.preferencesHandler.preferences.map<{ layout: LayoutConfig, definition: string }[]>(allPreferences => {
const installedThemes: { layout: Layout, definition: string }[] = []; const installedThemes: { layout: LayoutConfig, definition: string }[] = [];
if (allPreferences === undefined) { if (allPreferences === undefined) {
return installedThemes; return installedThemes;
} }
@ -208,18 +208,13 @@ export default class State {
const themename = allPreferencesKey.match(/^mapcomplete-installed-theme-(.*)-combined-length$/); const themename = allPreferencesKey.match(/^mapcomplete-installed-theme-(.*)-combined-length$/);
if (themename && themename[1] !== "") { if (themename && themename[1] !== "") {
const customLayout = self.osmConnection.GetLongPreference("installed-theme-" + themename[1]); const customLayout = self.osmConnection.GetLongPreference("installed-theme-" + themename[1]);
if(customLayout.data === undefined){ if (customLayout.data === undefined) {
console.log("No data defined for ", themename[1]); console.log("No data defined for ", themename[1]);
continue; continue;
} }
try { try {
const layout = State.FromBase64(customLayout.data); const layout = new LayoutConfig(
if(layout.id === undefined){ JSON.parse(btoa(customLayout.data)));
// This is an old style theme
// We remove it
customLayout.setData(undefined);
continue;
}
installedThemes.push({ installedThemes.push({
layout: layout, layout: layout,
definition: customLayout.data definition: customLayout.data
@ -257,10 +252,10 @@ export default class State {
if (layoutToUse === undefined) { if (layoutToUse === undefined) {
return; return;
} }
if (this.layoutToUse.data.supportedLanguages.indexOf(currentLanguage) < 0) { if (this.layoutToUse.data.language.indexOf(currentLanguage) < 0) {
console.log("Resetting language to", layoutToUse.supportedLanguages[0], "as", currentLanguage, " is unsupported") console.log("Resetting language to", layoutToUse.language[0], "as", currentLanguage, " is unsupported")
// The current language is not supported -> switch to a supported one // The current language is not supported -> switch to a supported one
Locale.language.setData(layoutToUse.supportedLanguages[0]); Locale.language.setData(layoutToUse.language[0]);
} }
}).ping() }).ping()
@ -288,5 +283,4 @@ export default class State {
} }
public static FromBase64 : (data: string) => Layout = undefined;
} }

View file

@ -6,11 +6,9 @@ import Combine from "./Base/Combine";
import {SubtleButton} from "./Base/SubtleButton"; import {SubtleButton} from "./Base/SubtleButton";
import State from "../State"; import State from "../State";
import {VariableUiElement} from "./Base/VariableUIElement"; import {VariableUiElement} from "./Base/VariableUIElement";
import {PersonalLayout} from "../Logic/PersonalLayout";
import {Layout} from "../Customizations/Layout";
import Svg from "../Svg"; import Svg from "../Svg";
import {Img} from "./Img"; import LayoutConfig from "../Customizations/JSON/LayoutConfig";
import * as personal from "../assets/themes/personalLayout/personalLayout.json"
export class MoreScreen extends UIElement { export class MoreScreen extends UIElement {
@ -21,7 +19,7 @@ export class MoreScreen extends UIElement {
this.ListenTo(State.state.installedThemes); this.ListenTo(State.state.installedThemes);
} }
private createLinkButton(layout: Layout, customThemeDefinition: string = undefined) { private createLinkButton(layout: LayoutConfig, customThemeDefinition: string = undefined) {
if (layout === undefined) { if (layout === undefined) {
return undefined; return undefined;
} }
@ -53,7 +51,7 @@ export class MoreScreen extends UIElement {
} }
let description = Translations.W(layout.description); let description = Translations.W(layout.shortDescription);
if (description !== undefined) { if (description !== undefined) {
description = new Combine(["<br/>", description]); description = new Combine(["<br/>", description]);
} }
@ -88,8 +86,8 @@ export class MoreScreen extends UIElement {
for (const k in AllKnownLayouts.allSets) { for (const k in AllKnownLayouts.allSets) {
const layout : Layout = AllKnownLayouts.allSets[k]; const layout : LayoutConfig = AllKnownLayouts.allSets[k];
if (k === PersonalLayout.NAME) { if (k === personal.id) {
if (State.state.osmConnection.userDetails.data.csCount < State.userJourney.personalLayoutUnlock) { if (State.state.osmConnection.userDetails.data.csCount < State.userJourney.personalLayoutUnlock) {
continue; continue;
} }

View file

@ -5,16 +5,15 @@ import Combine from "./Base/Combine";
import {VariableUiElement} from "./Base/VariableUIElement"; import {VariableUiElement} from "./Base/VariableUIElement";
import CheckBox from "./Input/CheckBox"; import CheckBox from "./Input/CheckBox";
import {VerticalCombine} from "./Base/VerticalCombine"; import {VerticalCombine} from "./Base/VerticalCombine";
import {Img} from "./Img";
import State from "../State"; import State from "../State";
import {Basemap} from "../Logic/Leaflet/Basemap"; import {Basemap} from "../Logic/Leaflet/Basemap";
import {FilteredLayer} from "../Logic/FilteredLayer"; import {FilteredLayer} from "../Logic/FilteredLayer";
import {Utils} from "../Utils"; import {Utils} from "../Utils";
import {UIEventSource} from "../Logic/UIEventSource"; import {UIEventSource} from "../Logic/UIEventSource";
import {SubtleButton} from "./Base/SubtleButton"; import {SubtleButton} from "./Base/SubtleButton";
import {Layout} from "../Customizations/Layout";
import Svg from "../Svg"; import Svg from "../Svg";
import {Translation} from "./i18n/Translation"; import {Translation} from "./i18n/Translation";
import LayoutConfig from "../Customizations/JSON/LayoutConfig";
export class ShareScreen extends UIElement { export class ShareScreen extends UIElement {
private readonly _options: UIElement; private readonly _options: UIElement;
@ -24,7 +23,7 @@ export class ShareScreen extends UIElement {
private readonly _linkStatus: UIEventSource<string | UIElement>; private readonly _linkStatus: UIEventSource<string | UIElement>;
private readonly _editLayout: UIElement; private readonly _editLayout: UIElement;
constructor(layout: Layout = undefined, layoutDefinition: string = undefined) { constructor(layout: LayoutConfig = undefined, layoutDefinition: string = undefined) {
super(undefined) super(undefined)
layout = layout ?? State.state?.layoutToUse?.data; layout = layout ?? State.state?.layoutToUse?.data;
layoutDefinition = layoutDefinition ?? State.state?.layoutDefinition; layoutDefinition = layoutDefinition ?? State.state?.layoutDefinition;
@ -164,12 +163,12 @@ export class ShareScreen extends UIElement {
}, optionParts); }, optionParts);
this.iframe = url.map(url => `&lt;iframe src="${url}" width="100%" height="100%" title="${layout.title?.InnerRender()??"MapComplete"} with MapComplete"&gt;&lt;/iframe&gt`); this.iframe = url.map(url => `&lt;iframe src="${url}" width="100%" height="100%" title="${layout?.title?.txt ?? "MapComplete"} with MapComplete"&gt;&lt;/iframe&gt`);
this._iframeCode = new VariableUiElement( this._iframeCode = new VariableUiElement(
url.map((url) => { url.map((url) => {
return `<span class='literal-code iframe-code-block'> return `<span class='literal-code iframe-code-block'>
&lt;iframe src="${url}" width="100%" height="100%" title="${layout.title?.InnerRender() ?? "MapComplete"} with MapComplete"&gt;&lt;/iframe&gt &lt;iframe src="${url}" width="100%" height="100%" title="${layout.title?.txt ?? "MapComplete"} with MapComplete"&gt;&lt;/iframe&gt
</span>` </span>`
}) })
); );

View file

@ -26,7 +26,7 @@ export class UserBadge extends UIElement {
constructor() { constructor() {
super(State.state.osmConnection.userDetails); super(State.state.osmConnection.userDetails);
this._userDetails = State.state.osmConnection.userDetails; this._userDetails = State.state.osmConnection.userDetails;
this._languagePicker = (LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.data.supportedLanguages) ?? new FixedUiElement("")) this._languagePicker = (LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.data.language) ?? new FixedUiElement(""))
.SetStyle("display:inline-block;width:min-content;"); .SetStyle("display:inline-block;width:min-content;");
this._loginButton = Translations.t.general.loginWithOpenStreetMap this._loginButton = Translations.t.general.loginWithOpenStreetMap

View file

@ -18,17 +18,24 @@ export class WelcomeMessage extends UIElement {
constructor() { constructor() {
super(State.state.osmConnection.userDetails); super(State.state.osmConnection.userDetails);
this.ListenTo(Locale.language); this.ListenTo(Locale.language);
this.languagePicker = LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.data.supportedLanguages, Translations.t.general.pickLanguage); this.languagePicker = LanguagePicker.CreateLanguagePicker(State.state.layoutToUse.data.language, Translations.t.general.pickLanguage);
const layout = State.state.layoutToUse.data; const layout = State.state.layoutToUse.data;
this.description =Translations.W(layout.welcomeMessage); this.description = new Combine([
"<h3>", layout.title, "</h3>",
layout.description
])
layout.descriptionTail
this.plzLogIn = this.plzLogIn =
Translations.W(layout.gettingStartedPlzLogin) Translations.t.general.loginWithOpenStreetMap
.onClick(() => { .onClick(() => {
State.state.osmConnection.AttemptLogin() State.state.osmConnection.AttemptLogin()
}); });
this.welcomeBack = Translations.W(layout.welcomeBackMessage); this.welcomeBack = Translations.t.general.welcomeBack;
this.tail = Translations.W(layout.welcomeTail); this.tail = layout.descriptionTail;
} }
InnerRender(): string { InnerRender(): string {

View file

@ -7,6 +7,24 @@ export class Translation extends UIElement {
private static forcedLanguage = undefined; private static forcedLanguage = undefined;
public readonly translations: object
constructor(translations: object, context?: string) {
super(Locale.language)
if(translations === undefined){
throw `Translation without content (${context})`
}
let count = 0;
for (const translationsKey in translations) {
count++;
}
this.translations = translations;
if(count === 0){
throw `No translations given in the object (${context})`
}
}
public Subs(text: any): Translation { public Subs(text: any): Translation {
const newTranslations = {}; const newTranslations = {};
for (const lang in this.translations) { for (const lang in this.translations) {
@ -56,7 +74,7 @@ export class Translation extends UIElement {
return this.translations[i]; // Return a random language return this.translations[i]; // Return a random language
} }
console.error("Missing language ", Locale.language.data, "for", this.translations) console.error("Missing language ", Locale.language.data, "for", this.translations)
return undefined; return "";
} }
@ -64,17 +82,6 @@ export class Translation extends UIElement {
return this.txt return this.txt
} }
public readonly translations: object
constructor(translations: object) {
super(Locale.language)
let count = 0;
for (const translationsKey in translations) {
count++;
}
this.translations = translations
}
public replace(a: string, b: string) { public replace(a: string, b: string) {
if (a.startsWith("{") && a.endsWith("}")) { if (a.startsWith("{") && a.endsWith("}")) {
a = a.substr(1, a.length - 2); a = a.substr(1, a.length - 2);

View file

@ -10,17 +10,6 @@ export default class Translations {
} }
static t = AllTranslationAssets.t; static t = AllTranslationAssets.t;
private static isTranslation(tr: any): boolean {
for (const key in tr) {
if (typeof tr[key] !== "string") {
return false;
}
}
return true;
}
public static W(s: string | UIElement): UIElement { public static W(s: string | UIElement): UIElement {
if (typeof (s) === "string") { if (typeof (s) === "string") {
return new FixedUiElement(s); return new FixedUiElement(s);

View file

@ -12,6 +12,7 @@
"language": [ "language": [
"en" "en"
], ],
"hideFromOverview": true,
"maintainer": "", "maintainer": "",
"icon": "./assets/layers/bike_monitoring_station/monitoring_station.svg", "icon": "./assets/layers/bike_monitoring_station/monitoring_station.svg",
"version": "0", "version": "0",

View file

@ -7,6 +7,9 @@
"shortDescription": { "shortDescription": {
"nl": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje" "nl": "Met deze tool kan je natuur in je buurt in kaart brengen en meer informatie geven over je favoriete plekje"
}, },
"hideFromOverview": true,
"enableShareScreen": false,
"enableMoreQuests": false,
"description": { "description": {
"nl": "<img style='float:right;margin: 1em;width: 10em;height: auto;' src='./assets/themes/buurtnatuur/groen_logo.svg' alt='logo-groen' class='logo'> <br /><b>Natuur maakt gelukkig.</b> Aan de hand van deze website willen we de natuur dicht bij ons beter inventariseren. Met als doel meer mensen te laten genieten van toegankelijke natuur én te strijden voor meer natuur in onze buurten. \n<ul><li>In welke natuurgebieden kan jij terecht? Hoe toegankelijk zijn ze?</li><li>In welke bossen kan een gezin in jouw gemeente opnieuw op adem komen?</li><li>Op welke onbekende plekjes is het zalig spelen?</li></ul><p>Samen kleuren we heel Vlaanderen en Brussel groen.</p>Blijf op de hoogte van de resultaten van buurtnatuur.be: <a href=\"https://www.groen.be/buurtnatuur\" target='_blank'>meld je aan voor e-mailupdates</a>." "nl": "<img style='float:right;margin: 1em;width: 10em;height: auto;' src='./assets/themes/buurtnatuur/groen_logo.svg' alt='logo-groen' class='logo'> <br /><b>Natuur maakt gelukkig.</b> Aan de hand van deze website willen we de natuur dicht bij ons beter inventariseren. Met als doel meer mensen te laten genieten van toegankelijke natuur én te strijden voor meer natuur in onze buurten. \n<ul><li>In welke natuurgebieden kan jij terecht? Hoe toegankelijk zijn ze?</li><li>In welke bossen kan een gezin in jouw gemeente opnieuw op adem komen?</li><li>Op welke onbekende plekjes is het zalig spelen?</li></ul><p>Samen kleuren we heel Vlaanderen en Brussel groen.</p>Blijf op de hoogte van de resultaten van buurtnatuur.be: <a href=\"https://www.groen.be/buurtnatuur\" target='_blank'>meld je aan voor e-mailupdates</a>."
}, },

View file

@ -7,9 +7,9 @@
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="123" width="98"
height="123" height="98"
viewBox="0 0 123 123" viewBox="0 0 98 98"
version="1.1" version="1.1"
id="svg42" id="svg42"
sodipodi:docname="logo.svg" sodipodi:docname="logo.svg"
@ -36,17 +36,17 @@
guidetolerance="10" guidetolerance="10"
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1680" inkscape:window-width="1920"
inkscape:window-height="1013" inkscape:window-height="1001"
id="namedview44" id="namedview44"
showgrid="false" showgrid="false"
inkscape:zoom="4" inkscape:zoom="4"
inkscape:cx="51.676089" inkscape:cx="-2.2900136"
inkscape:cy="85.90359" inkscape:cy="62.988337"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
inkscape:current-layer="layer1" /> inkscape:current-layer="layer4" />
<defs <defs
id="defs40"> id="defs40">
<filter <filter
@ -105,12 +105,7 @@
id="layer4" id="layer4"
inkscape:label="pin" inkscape:label="pin"
style="display:inline" style="display:inline"
sodipodi:insensitive="true"> transform="translate(-12.466103,-2.0847473)">
<path
d="m 66.510602,115.17875 c -1.7831,3.887 -7.3059,3.887 -9.089,0 l -30.7431,-67.009004 c -1.5196,-3.3121 0.9005,-7.085 4.5445,-7.085 h 61.4862 c 3.644,0 6.0641,3.7729 4.5445,7.085 z"
id="path2"
inkscape:connector-curvature="0"
style="display:inline;fill:#e2783d" />
<circle <circle
cx="61.466103" cx="61.466103"
cy="51.084747" cy="51.084747"
@ -122,7 +117,8 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
inkscape:label="re" inkscape:label="re"
style="display:inline"> style="display:inline"
transform="translate(-12.466103,-2.0847473)">
<rect <rect
style="fill:#fffcff;fill-opacity:1;stroke:#ffffff;stroke-width:3.50900006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" style="fill:#fffcff;fill-opacity:1;stroke:#ffffff;stroke-width:3.50900006;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect834" id="rect834"

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -0,0 +1,22 @@
{
"id": "drinking_water",
"title": {
"en": "Drinking Water",
"nl": "Drinkwaterpunten"
},
"description": {
"en": "On this map, publicly accessible drinkging water spots are shown and can be easily added",
"nl": "Op deze kaart staan publiek toegankelijke drinkwaterpunten en kan je makkelijk een nieuw drinkwaterpunt toevoegen",
},
"language": ["en", "nl"],
"maintainer": "MapComplete",
"icon": "./assets/themes/drinking_water/logo.svg",
"version": "0",
"startLat": 50.8465573,
"defaultBackgroundId": "CartoDB.Voyager",
"startLon": 4.3516970,
"startZoom": 16,
"widenFactor": 0.05,
"layers": ["drinking_water"],
"roamingRenderings": []
}

View file

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="98"
height="98"
viewBox="0 0 98 98"
version="1.1"
id="svg27"
sodipodi:docname="logo.svg"
style="fill:none"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
<metadata
id="metadata31">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="697"
inkscape:window-height="480"
id="namedview29"
showgrid="false"
inkscape:zoom="1.9032258"
inkscape:cx="49"
inkscape:cy="36"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg27" />
<circle
cx="49"
cy="49"
r="49"
id="circle4"
style="fill:#6bc4f7" />
<g
id="g8"
style="filter:url(#filter0_d)">
<path
d="M 79,41.8705 C 79,39.3976 77.4079,35.6726 74.1327,30.4823 71.8064,26.7956 69.5171,23.8005 69.4208,23.6747 69.0964,23.2513 68.5726,23 68.0144,23 c -0.5582,0 -1.0822,0.2514 -1.4066,0.6748 -0.2291,0.299 -4.7798,6.2642 -7.5136,11.694 C 54.8849,28.8794 50.9494,23.7307 50.9067,23.6748 50.5821,23.2513 50.0582,23 49.5,23 c -0.5582,0 -1.0821,0.2513 -1.4066,0.6747 -0.0466,0.061 -4.7264,6.1834 -9.3404,13.4954 -0.4943,0.7835 -0.9674,1.5491 -1.4235,2.3005 -2.3812,-3.8202 -4.8133,-7.0045 -4.9373,-7.1663 -0.3245,-0.4234 -0.8484,-0.6747 -1.4066,-0.6747 -0.5582,0 -1.082,0.2513 -1.4065,0.6747 -0.0963,0.1257 -2.3856,3.1209 -4.7119,6.8076 C 21.5921,44.3021 20,48.0271 20,50.5 c 0,5.1193 4.0504,9.3732 9.3205,10.1234 C 30.1402,70.3329 38.8775,78 49.5,78 c 11.1619,0 20.2429,-8.4653 20.2429,-18.8705 0,-1.8756 -0.5352,-4.2206 -1.598,-7.0198 C 74.1423,52.0443 79,47.4766 79,41.8705 Z M 23.457,50.5 c 0,-1.043 0.5687,-3.6947 4.3774,-9.7344 1.1411,-1.8094 2.2916,-3.4709 3.1512,-4.6727 1.172,1.6389 2.8862,4.1349 4.3885,6.7079 -3.4518,6.0994 -5.4558,10.9816 -5.9787,14.5599 C 26.0053,56.6784 23.457,53.8613 23.457,50.5 Z M 49.5,74.7773 c -9.2557,0 -16.7858,-7.0195 -16.7858,-15.6478 0,-2.7503 1.5624,-8.5056 9.0059,-20.3054 3.0174,-4.7834 6.0639,-9.0496 7.7799,-11.3887 1.713,2.3348 4.7519,6.5904 7.7673,11.3688 7.4539,11.8125 9.0184,17.5731 9.0184,20.3254 10e-5,8.6282 -7.53,15.6477 -16.7857,15.6477 z M 68.0144,48.8887 c -0.4308,0 -0.8584,-0.0338 -1.2782,-0.1008 -1.3649,-2.9346 -3.199,-6.2819 -5.4645,-9.9716 1.536,-3.812 4.8482,-8.7016 6.7432,-11.3525 0.8558,1.1963 1.9999,2.8487 3.1381,4.6524 3.8197,6.0532 4.39,8.7095 4.39,9.7543 0,3.8697 -3.3774,7.0182 -7.5286,7.0182 z"
id="path6"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
</g>
<defs
id="defs25">
<filter
id="filter0_d"
x="16"
y="23"
width="67"
height="63"
filterUnits="userSpaceOnUse"
style="color-interpolation-filters:sRGB">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood10" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
id="feColorMatrix12" />
<feOffset
dy="4"
id="feOffset14" />
<feGaussianBlur
stdDeviation="2"
id="feGaussianBlur16" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"
id="feColorMatrix18" />
<feBlend
mode="normal"
in2="BackgroundImageFix"
result="effect1_dropShadow"
id="feBlend20" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_dropShadow"
result="shape"
id="feBlend22" />
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View file

@ -251,6 +251,5 @@
"wayHandling": 1 "wayHandling": 1
} }
], ],
"roamingRenderings": [], "roamingRenderings": []
"shortDescription": {}
} }

View file

@ -0,0 +1,37 @@
{
"id": "personal",
"title": {
"en": "Personal theme",
"nl": "Persoonlijk thema",
"es": "Interficie personal",
"ca": "Interfície personal",
"gl": "Tema personalizado",
"de": "Persönliches Thema"
},
"description": {
"en": "Create a personal theme based on all the available layers of all themes",
"es": "Crea una interficie basada en todas las capas disponibles de todas las interficies",
"ca": "Crea una interfície basada en totes les capes disponibles de totes les interfícies",
"gl": "Crea un tema baseado en todas as capas dispoñíbeis de todos os temas",
"de": "Erstellen Sie ein persönliches Thema auf der Grundlage aller verfügbaren Ebenen aller Themen"
},
"language": [
"en",
"nl",
"es",
"ca",
"gl",
"de"
],
"maintainer": "MapComplete",
"icon": "./assets/svg/addSmall.svg",
"version": "0",
"startLat": 0,
"startLon": 0,
"startZoom": 16,
"widenFactor": 0.05,
"layers": [
"drinking_water"
],
"roamingRenderings": []
}

View file

@ -12,6 +12,15 @@
"language": [ "language": [
"nl" "nl"
], ],
"hideFromOverview": true,
"enableUserBadge": false,
"enableShareScreen":false,
"enableMoreQuests": false,
"enableLayers":false,
"enableSearch": false,
"enableGeolocation":false,
"maintainer": "", "maintainer": "",
"icon": "./assets/themes/widths/icon.svg", "icon": "./assets/themes/widths/icon.svg",
"version": "0", "version": "0",

View file

@ -886,21 +886,6 @@
} }
}, },
"favourite": { "favourite": {
"title": {
"en": "Personal theme",
"nl": "Persoonlijk thema",
"es": "Interficie personal",
"ca": "Interfície personal",
"gl": "Tema personalizado",
"de": "Persönliches Thema"
},
"description": {
"en": "Create a personal theme based on all the available layers of all themes",
"es": "Crea una interficie basada en todas las capas disponibles de todas las interficies",
"ca": "Crea una interfície basada en totes les capes disponibles de totes les interfícies",
"gl": "Crea un tema baseado en todas as capas dispoñíbeis de todos os temas",
"de": "Erstellen Sie ein persönliches Thema auf der Grundlage aller verfügbaren Ebenen aller Themen"
},
"panelIntro": { "panelIntro": {
"en": "<h3>Your personal theme</h3>Activate your favourite layers from all the official themes", "en": "<h3>Your personal theme</h3>Activate your favourite layers from all the official themes",
"ca": "<h3>La teva interfície personal</h3>Activa les teves capes favorites de totes les interfícies oficials", "ca": "<h3>La teva interfície personal</h3>Activa les teves capes favorites de totes les interfícies oficials",

View file

@ -1,22 +1,22 @@
import {Img} from "./UI/Img" import {Img} from "./UI/Img"
Img.runningFromConsole = true;
import {UIElement} from "./UI/UIElement"; import {UIElement} from "./UI/UIElement";
// We HAVE to mark this while importing
UIElement.runningFromConsole = true;
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import {Layout} from "./Customizations/Layout";
import {readFileSync, writeFile, writeFileSync} from "fs"; import {readFileSync, writeFile, writeFileSync} from "fs";
import Locale from "./UI/i18n/Locale"; import Locale from "./UI/i18n/Locale";
import svg2img from 'promise-svg2img'; import svg2img from 'promise-svg2img';
import Translations from "./UI/i18n/Translations"; import Translations from "./UI/i18n/Translations";
import {Translation} from "./UI/i18n/Translation"; import {Translation} from "./UI/i18n/Translation";
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
Img.runningFromConsole = true;
// We HAVE to mark this while importing
UIElement.runningFromConsole = true;
function enc(str: string): string { function enc(str: string): string {
return encodeURIComponent(str.toLowerCase()); return encodeURIComponent(str.toLowerCase());
} }
function validate(layout: Layout) { function validate(layout: LayoutConfig) {
const translations: Translation[] = []; const translations: Translation[] = [];
const queue: any[] = [layout] const queue: any[] = [layout]
@ -38,7 +38,7 @@ function validate(layout: Layout) {
const missing = {} const missing = {}
const present = {} const present = {}
for (const ln of layout.supportedLanguages) { for (const ln of layout.language) {
missing[ln] = 0; missing[ln] = 0;
present[ln] = 0; present[ln] = 0;
for (const translation of translations) { for (const translation of translations) {
@ -58,7 +58,7 @@ function validate(layout: Layout) {
let message = `Translation completenes for theme ${layout.id}` let message = `Translation completenes for theme ${layout.id}`
let isComplete = true; let isComplete = true;
for (const ln of layout.supportedLanguages) { for (const ln of layout.language) {
const amiss = missing[ln]; const amiss = missing[ln];
const ok = present[ln]; const ok = present[ln];
const total = amiss + ok; const total = amiss + ok;
@ -75,11 +75,11 @@ function validate(layout: Layout) {
} }
function generateWikiEntry(layout: Layout){ function generateWikiEntry(layout: LayoutConfig){
if(layout.hideFromOverview){ if(layout.hideFromOverview){
return ""; return "";
} }
const languages = layout.supportedLanguages.map(ln => `{{#language:${ln}|en}}`).join(", ") const languages = layout.language.map(ln => `{{#language:${ln}|en}}`).join(", ")
let auth = "Yes"; let auth = "Yes";
if(layout.maintainer !== "" && layout.maintainer !== "MapComplete"){ if(layout.maintainer !== "" && layout.maintainer !== "MapComplete"){
auth=`Yes, by ${layout.maintainer};` auth=`Yes, by ${layout.maintainer};`
@ -97,7 +97,7 @@ function generateWikiEntry(layout: Layout){
const alreadyWritten = [] const alreadyWritten = []
function createIcon(iconPath: string, size: number, layout: Layout) { function createIcon(iconPath: string, size: number, layout: LayoutConfig) {
let name = iconPath.split(".").slice(0, -1).join("."); let name = iconPath.split(".").slice(0, -1).join(".");
if (name.startsWith("./")) { if (name.startsWith("./")) {
name = name.substr(2) name = name.substr(2)
@ -138,7 +138,7 @@ function createIcon(iconPath: string, size: number, layout: Layout) {
return newname; return newname;
} }
function createManifest(layout: Layout, relativePath: string) { function createManifest(layout: LayoutConfig, relativePath: string) {
const name = layout.id; const name = layout.id;
const icons = []; const icons = [];
@ -191,9 +191,9 @@ function createManifest(layout: Layout, relativePath: string) {
} }
const template = readFileSync("index.html", "utf8"); const template = readFileSync("index.html", "utf8");
function createLandingPage(layout: Layout) { function createLandingPage(layout: LayoutConfig) {
Locale.language.setData(layout.supportedLanguages[0]); Locale.language.setData(layout.language[0]);
const ogTitle = Translations.W(layout.title)?.InnerRender(); const ogTitle = Translations.W(layout.title)?.InnerRender();
const ogDescr = Translations.W(layout.description ?? "Easily add and edit geodata with OpenStreetMap")?.InnerRender(); const ogDescr = Translations.W(layout.description ?? "Easily add and edit geodata with OpenStreetMap")?.InnerRender();

View file

@ -1,11 +1,11 @@
import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
import {Layout} from "./Customizations/Layout";
import {FixedUiElement} from "./UI/Base/FixedUiElement"; import {FixedUiElement} from "./UI/Base/FixedUiElement";
import {InitUiElements} from "./InitUiElements"; import {InitUiElements} from "./InitUiElements";
import {QueryParameters} from "./Logic/Web/QueryParameters"; import {QueryParameters} from "./Logic/Web/QueryParameters";
import {UIEventSource} from "./Logic/UIEventSource"; import {UIEventSource} from "./Logic/UIEventSource";
import * as $ from "jquery"; import * as $ from "jquery";
import SharedLayers from "./Customizations/SharedLayers"; import SharedLayers from "./Customizations/SharedLayers";
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
let defaultLayout = "bookcases" let defaultLayout = "bookcases"
// --------------------- Special actions based on the parameters ----------------- // --------------------- Special actions based on the parameters -----------------
@ -57,7 +57,7 @@ if (path !== "index.html" && path !== "") {
// Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default // Run over all questsets. If a part of the URL matches a searched-for part in the layout, it'll take that as the default
for (const k in AllKnownLayouts.allSets) { for (const k in AllKnownLayouts.allSets) {
const layout = AllKnownLayouts.allSets[k]; const layout : LayoutConfig= AllKnownLayouts.allSets[k];
const possibleParts = (layout.locationContains ?? []); const possibleParts = (layout.locationContains ?? []);
for (const locationMatch of possibleParts) { for (const locationMatch of possibleParts) {
if (locationMatch === "") { if (locationMatch === "") {
@ -71,7 +71,7 @@ for (const k in AllKnownLayouts.allSets) {
defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout).data; defaultLayout = QueryParameters.GetQueryParameter("layout", defaultLayout).data;
let layoutToUse: Layout = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?? AllKnownLayouts["all"]; let layoutToUse: LayoutConfig = AllKnownLayouts.allSets[defaultLayout.toLowerCase()] ?? AllKnownLayouts["all"];
const userLayoutParam = QueryParameters.GetQueryParameter("userlayout", "false"); const userLayoutParam = QueryParameters.GetQueryParameter("userlayout", "false");
@ -94,8 +94,9 @@ if (layoutFromBase64.startsWith("wiki:")) {
.firstChild.textContent; .firstChild.textContent;
try { try {
console.log("DOWNLOADED:",layoutJson); console.log("DOWNLOADED:",layoutJson);
const layout = Layout.LayoutFromJSON(JSON.parse(layoutJson), SharedLayers.sharedLayers); const parsed = JSON.parse(layoutJson);
layout.id = layoutFromBase64; parsed["id"] = layoutFromBase64
const layout =new LayoutConfig(parsed);
InitUiElements.InitAll(layout, layoutFromBase64, testing, layoutFromBase64, btoa(layoutJson)); InitUiElements.InitAll(layout, layoutFromBase64, testing, layoutFromBase64, btoa(layoutJson));
} catch (e) { } catch (e) {
new FixedUiElement(`<a href="${cleanUrl}">${themeName}</a> is invalid:<br/>${e}`) new FixedUiElement(`<a href="${cleanUrl}">${themeName}</a> is invalid:<br/>${e}`)