diff --git a/Customizations/JSON/LayerConfig.ts b/Customizations/JSON/LayerConfig.ts
index 268fa9817..533658b8c 100644
--- a/Customizations/JSON/LayerConfig.ts
+++ b/Customizations/JSON/LayerConfig.ts
@@ -8,23 +8,33 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson";
import {Translation} from "../../UI/i18n/Translation";
import {Img} from "../../UI/Img";
import Svg from "../../Svg";
+import {SubstitutedTranslation} from "../../UI/SpecialVisualizations";
+import {Utils} from "../../Utils";
+import Combine from "../../UI/Base/Combine";
+import {Browser} from "leaflet";
export default class LayerConfig {
+
+
id: string;
name: Translation
description: Translation;
overpassTags: TagsFilter;
+ doNotDownload: boolean;
+
+ passAllFeatures: boolean;
minzoom: number;
- title: TagRenderingConfig;
+ title?: TagRenderingConfig;
titleIcons: TagRenderingConfig[];
icon: TagRenderingConfig;
iconSize: TagRenderingConfig;
+ rotation: TagRenderingConfig;
color: TagRenderingConfig;
width: TagRenderingConfig;
dashArray: TagRenderingConfig;
@@ -53,10 +63,11 @@ export default class LayerConfig {
this.name = Translations.T(json.name);
this.description = Translations.T(json.name);
this.overpassTags = FromJSON.Tag(json.overpassTags, context + ".overpasstags");
+ this.doNotDownload = json.doNotDownload ?? false,
+ this.passAllFeatures = json.passAllFeatures ?? false;
this.minzoom = json.minzoom;
this.wayHandling = json.wayHandling ?? 0;
this.hideUnderlayingFeaturesMinPercentage = json.hideUnderlayingFeaturesMinPercentage ?? 0;
- this.title = new TagRenderingConfig(json.title);
this.presets = (json.presets ?? []).map(pr =>
({
title: Translations.T(pr.title),
@@ -93,7 +104,10 @@ export default class LayerConfig {
function tr(key, deflt) {
const v = json[key];
- if (v === undefined) {
+ if (v === undefined || v === null) {
+ if (deflt === undefined) {
+ return undefined;
+ }
return new TagRenderingConfig(deflt);
}
if (typeof v === "string") {
@@ -107,11 +121,19 @@ export default class LayerConfig {
}
- this.title = tr("title", "");
+ this.title = tr("title", undefined);
this.icon = tr("icon", Img.AsData(Svg.bug));
+ const iconPath = this.icon.GetRenderValue({id: "node/-1"}).txt;
+ if (iconPath.startsWith(Utils.assets_path)) {
+ const iconKey = iconPath.substr(Utils.assets_path.length);
+ if (Svg.All[iconKey] === undefined) {
+ throw "Builtin SVG asset not found: " + iconPath
+ }
+ }
this.iconSize = tr("iconSize", "40,40,center");
this.color = tr("color", "#0000ff");
this.width = tr("width", "7");
+ this.rotation = tr("rotation", "0");
this.dashArray = tr("dashArray", "");
@@ -121,13 +143,16 @@ export default class LayerConfig {
public GenerateLeafletStyle(tags: any):
{
color: string;
- icon: { popupAnchor: [number, number]; iconAnchor: [number, number]; iconSize: [number, number]; iconUrl: string }; weight: number; dashArray: number[]
+ icon: {
+ iconUrl: string,
+ popupAnchor: [number, number];
+ iconAnchor: [number, number];
+ iconSize: [number, number];
+ html: string;
+ rotation: number;
+ };
+ weight: number; dashArray: number[]
} {
- const iconUrl = this.icon?.GetRenderValue(tags)?.txt;
- const iconSize = (this.iconSize?.GetRenderValue(tags)?.txt ?? "40,40,center").split(",");
-
-
- const dashArray = this.dashArray.GetRenderValue(tags)?.txt.split(" ").map(Number);
function num(str, deflt = 40) {
const n = Number(str);
@@ -137,6 +162,33 @@ export default class LayerConfig {
return n;
}
+ function rendernum(tr: TagRenderingConfig, deflt: number) {
+ const str = Number(render(tr, "" + deflt));
+ const n = Number(str);
+ if (isNaN(n)) {
+ return deflt;
+ }
+ return n;
+ }
+
+ function render(tr: TagRenderingConfig, deflt?: string) {
+ const str = (tr?.GetRenderValue(tags)?.txt ?? deflt);
+ return SubstitutedTranslation.SubstituteKeys(str, tags);
+ }
+
+ const iconUrl = render(this.icon);
+ const iconSize = render(this.iconSize, "40,40,center").split(",");
+ const dashArray = render(this.dashArray).split(" ").map(Number);
+ let color = render(this.color, "#00f");
+
+ if (color.startsWith("--")) {
+ color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color")
+ }
+
+ const weight = rendernum(this.width, 5);
+ const rotation = rendernum(this.rotation, 0);
+
+
const iconW = num(iconSize[0]);
const iconH = num(iconSize[1]);
const mode = iconSize[2] ?? "center"
@@ -157,16 +209,22 @@ export default class LayerConfig {
anchorH = iconH;
}
-
- const color = this.color?.GetRenderValue(tags)?.txt ?? "#00f";
- let weight = num(this.width?.GetRenderValue(tags)?.txt, 5);
+ let html = ``;
+ if (iconUrl.startsWith(Utils.assets_path)) {
+ const key = iconUrl.substr(Utils.assets_path.length);
+ html = new Combine([
+ (Svg.All[key] as string).replace(/stop-color:#000000/g, 'stop-color:' + color)
+ ]).SetStyle(`width:100%;height:100%;rotate:${rotation}deg;display:block;`).Render();
+ }
return {
icon:
{
- iconUrl: iconUrl,
+ html: html,
iconSize: [iconW, iconH],
iconAnchor: [anchorW, anchorH],
- popupAnchor: [0, 3 - anchorH]
+ popupAnchor: [0, 3 - anchorH],
+ rotation: rotation,
+ iconUrl: iconUrl
},
color: color,
weight: weight,
diff --git a/Customizations/JSON/LayerConfigJson.ts b/Customizations/JSON/LayerConfigJson.ts
index be70638fc..b7371c538 100644
--- a/Customizations/JSON/LayerConfigJson.ts
+++ b/Customizations/JSON/LayerConfigJson.ts
@@ -29,6 +29,12 @@ export interface LayerConfigJson {
*/
overpassTags: AndOrTagConfigJson | string;
+ /**
+ * If set, this layer will not query overpass; but it'll still match the tags above which are by chance returned by other layers.
+ * Works well together with 'passAllFeatures', to add decoration
+ */
+ doNotDownload?: boolean;
+
/**
* The zoomlevel at which point the data is shown and loaded.
*/
@@ -39,8 +45,13 @@ export interface LayerConfigJson {
/**
* The title shown in a popup for elements of this layer.
*/
- title: string | TagRenderingConfigJson;
-
+ title?: string | TagRenderingConfigJson;
+
+ /**
+ * Small icons shown next to the title.
+ * If not specified, the OsmLink and wikipedia links will be used by default.
+ * Use an empty array to hide them
+ */
titleIcons?: (string | TagRenderingConfigJson)[];
/**
@@ -54,9 +65,14 @@ export interface LayerConfigJson {
* Default is '40,40,center'
*/
iconSize?: string | TagRenderingConfigJson;
-
/**
- * The color for way-elements
+ * The rotation of an icon, useful for e.g. directions
+ */
+ rotation?: string | TagRenderingConfigJson;
+
+ /**
+ * The color for way-elements and SVG-elements.
+ * If the value starts with "--", the style of the body element will be queried for the corresponding variable instead
*/
color?: string | TagRenderingConfigJson;
/**
@@ -87,6 +103,11 @@ export interface LayerConfigJson {
*/
hideUnderlayingFeaturesMinPercentage?:number;
+ /**
+ * If set, this layer will pass all the features it receives onto the next layer
+ */
+ passAllFeatures?:boolean
+
/**
* Presets for this layer
*/
@@ -98,6 +119,7 @@ export interface LayerConfigJson {
/**
* All the tag renderings.
+ * A tag rendering is a block that either shows the known value or asks a question.
*/
tagRenderings?: (string | TagRenderingConfigJson) []
}
\ No newline at end of file
diff --git a/Customizations/JSON/LayoutConfigJson.ts b/Customizations/JSON/LayoutConfigJson.ts
index 95c14c419..fe5451f02 100644
--- a/Customizations/JSON/LayoutConfigJson.ts
+++ b/Customizations/JSON/LayoutConfigJson.ts
@@ -102,7 +102,19 @@ export interface LayoutConfigJson {
/**
- * The layers to display
+ * The layers to display.
+ *
+ * Every layer contains a description of which feature to display - the overpassTags which are queried.
+ * Instead of running one query for every layer, the query is fused.
+ *
+ * Afterwards, every layer is given the list of features.
+ * Every layer takes away the features that match with them*, and give the leftovers to the next layers.
+ *
+ * This implies that the _order_ of the layers is important in the case of features with the same tags;
+ * as the later layers might never receive their feature.
+ *
+ * *layers can also remove 'leftover'-features if the leftovers overlap with a feature in the layer itself
+ *
*/
layers: (LayerConfigJson | string)[],
diff --git a/Customizations/SharedLayers.ts b/Customizations/SharedLayers.ts
index 34dd9daf9..202b28985 100644
--- a/Customizations/SharedLayers.ts
+++ b/Customizations/SharedLayers.ts
@@ -13,6 +13,7 @@ import * as bike_shops from "../assets/layers/bike_shop/bike_shop.json"
import * as bike_cleaning from "../assets/layers/bike_cleaning/bike_cleaning.json"
import * as maps from "../assets/layers/maps/maps.json"
import * as information_boards from "../assets/layers/information_board/information_board.json"
+import * as direction from "../assets/layers/direction/direction.json"
import LayerConfig from "./JSON/LayerConfig";
export default class SharedLayers {
@@ -37,6 +38,7 @@ export default class SharedLayers {
new LayerConfig(bike_shops, "shared_layers"),
new LayerConfig(bike_cleaning, "shared_layers"),
new LayerConfig(maps, "shared_layers"),
+ new LayerConfig(direction, "shared_layers"),
new LayerConfig(information_boards, "shared_layers")
];
diff --git a/InitUiElements.ts b/InitUiElements.ts
index 20af0c885..e4ac7026a 100644
--- a/InitUiElements.ts
+++ b/InitUiElements.ts
@@ -35,6 +35,9 @@ import Svg from "./Svg";
import Link from "./UI/Base/Link";
import * as personal from "./assets/themes/personalLayout/personalLayout.json"
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
+import * as L from "leaflet";
+import {Img} from "./UI/Img";
+import {UserDetails} from "./Logic/Osm/OsmConnection";
export class InitUiElements {
@@ -142,6 +145,7 @@ export class InitUiElements {
}
+
if (layoutToUse.id === personal.id) {
State.state.favouriteLayers.addCallback(updateFavs);
State.state.installedThemes.addCallback(updateFavs);
@@ -153,6 +157,10 @@ export class InitUiElements {
* This is given to the div which renders fullscreen on mobile devices
*/
State.state.selectedElement.addCallback((feature) => {
+
+ if (feature === undefined) {
+ State.state.fullScreenMessage.setData(undefined);
+ }
if (feature?.properties === undefined) {
return;
}
@@ -163,17 +171,22 @@ export class InitUiElements {
continue;
}
const applicable = layer.overpassTags.matches(TagUtils.proprtiesToKV(data));
- if (applicable) {
- // This layer is the layer that gives the questions
-
- const featureBox = new FeatureInfoBox(
- State.state.allElements.getElement(data.id),
- layer
- );
-
- State.state.fullScreenMessage.setData(featureBox);
- break;
+ if (!applicable) {
+ continue;
}
+
+ if(layer.title === null && layer.tagRenderings.length === 0){
+ continue;
+ }
+
+ // This layer is the layer that gives the questions
+ const featureBox = new FeatureInfoBox(
+ State.state.allElements.getElement(data.id),
+ layer
+ );
+
+ State.state.fullScreenMessage.setData(featureBox);
+ break;
}
}
);
@@ -204,6 +217,21 @@ export class InitUiElements {
content.AttachTo("messagesbox");
}
+ State.state.osmConnection.userDetails.map((userDetails: UserDetails) => userDetails?.home)
+ .addCallbackAndRun(home => {
+ if (home === undefined) {
+ return;
+ }
+ const color = getComputedStyle(document.body).getPropertyValue("--subtle-detail-color")
+ const icon = L.icon({
+ iconUrl: Img.AsData(Svg.home_white_bg.replace(/#ffffff/g, color)),
+ iconSize: [30, 30],
+ iconAnchor: [15, 15]
+ });
+ const marker = L.marker([home.lat, home.lon], {icon: icon})
+ marker.addTo(State.state.bm.map)
+ console.log(marker)
+ });
new GeoLocationHandler()
.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;`)
@@ -327,6 +355,10 @@ export class InitUiElements {
checkbox.isEnabled.setData(false);
})
+ State.state.selectedElement.addCallback(() => {
+ checkbox.isEnabled.setData(false);
+ })
+
const fullOptions2 = this.CreateWelcomePane();
State.state.fullScreenMessage.setData(fullOptions2)
@@ -435,13 +467,15 @@ export class InitUiElements {
return new Combine([mapComplete, reportBug, " | ", stats, " | ", editHere, editWithJosm]).Render();
}, [State.state.osmConnection.userDetails])
-
).SetClass("map-attribution")
}
static InitBaseMap() {
const bm = new Basemap("leafletDiv", State.state.locationControl, this.CreateAttribution());
State.state.bm = bm;
+ bm.map.on("popupclose", () => {
+ State.state.selectedElement.setData(undefined)
+ })
State.state.layerUpdater = new UpdateFromOverpass(State.state);
State.state.availableBackgroundLayers = new AvailableBaseLayers(State.state).availableEditorLayers;
@@ -475,13 +509,12 @@ export class InitUiElements {
throw "Layer " + layer + " was not substituted";
}
- const flayer: FilteredLayer = new FilteredLayer(layer,
- (tagsES) => {
- return new FeatureInfoBox(
- tagsES,
- layer,
- )
- });
+ let generateContents = (tags: UIEventSource) => new FeatureInfoBox(tags, layer);
+ if (layer.title === undefined && (layer.tagRenderings ?? []).length === 0) {
+ generateContents = undefined;
+ }
+
+ const flayer: FilteredLayer = new FilteredLayer(layer, generateContents);
flayers.push(flayer);
QueryParameters.GetQueryParameter("layer-" + layer.id, "true", "Wehter or not layer " + layer.id + " is shown")
diff --git a/Logic/FilteredLayer.ts b/Logic/FilteredLayer.ts
index ac70ae59c..bafdc28eb 100644
--- a/Logic/FilteredLayer.ts
+++ b/Logic/FilteredLayer.ts
@@ -6,6 +6,7 @@ import {GeoOperations} from "./GeoOperations";
import {UIElement} from "../UI/UIElement";
import State from "../State";
import LayerConfig from "../Customizations/JSON/LayerConfig";
+import Hash from "./Web/Hash";
/***
* A filtered layer is a layer which offers a 'set-data' function
@@ -75,11 +76,13 @@ export class FilteredLayer {
const selfFeatures = [];
for (let feature of geojson.features) {
const tags = TagUtils.proprtiesToKV(feature.properties);
- if (!this.filters.matches(tags)) {
- leftoverFeatures.push(feature);
- continue;
+ const matches = this.filters.matches(tags);
+ if (matches) {
+ selfFeatures.push(feature);
+ }
+ if (!matches || this.layerDef.passAllFeatures) {
+ leftoverFeatures.push(feature);
}
- selfFeatures.push(feature);
}
this.RenderLayer(selfFeatures)
@@ -117,7 +120,6 @@ export class FilteredLayer {
// We fetch all the data we have to show:
let fusedFeatures = this.ApplyWayHandling(this.FuseData(features));
- console.log("Fused:",fusedFeatures)
// And we copy some features as points - if needed
const data = {
@@ -126,7 +128,6 @@ export class FilteredLayer {
}
let self = this;
- console.log(data);
this._geolayer = L.geoJSON(data, {
style: feature =>
self.layerDef.GenerateLeafletStyle(feature.properties),
@@ -147,19 +148,21 @@ export class FilteredLayer {
color: style.color
});
} else {
- if (style.icon.iconSize === undefined) {
- style.icon.iconSize = [50, 50]
- }
-
marker = L.marker(latLng, {
- icon: L.icon(style.icon)
+ icon: L.divIcon(style.icon)
});
}
return marker;
},
onEachFeature: function (feature, layer: Layer) {
- layer.on("click", (e) => {
+ if (self._showOnPopup === undefined) {
+ // No popup contents defined -> don't do anything
+ return;
+ }
+
+
+ function openPopup(latlng: any) {
if (layer.getPopup() === undefined
&& (window.screen.availHeight > 600 || window.screen.availWidth > 600) // We DON'T trigger this code on small screens! No need to create a popup
) {
@@ -168,30 +171,47 @@ export class FilteredLayer {
closeOnEscapeKey: true,
}, layer);
- // @ts-ignore
- popup.setLatLng(e.latlng)
+ popup.setLatLng(latlng)
+
+ layer.bindPopup(popup);
+ const eventSource = State.state.allElements.addOrGetElement(feature);
+ const uiElement = self._showOnPopup(eventSource, feature);
+ // We first render the UIelement (which'll still need an update later on...)
+ // But at least it'll be visible already
+ popup.setContent(uiElement.Render());
+ popup.openOn(State.state.bm.map);
+ // popup.openOn(State.state.bm.map);
+ // ANd we perform the pending update
+ uiElement.Update();
+ // @ts-ignore
+ popup.Update = () => {
+ uiElement.Update();
+ }
+ } else {
+ // @ts-ignore
+ layer.getPopup().Update();
+ }
+
+
+ // We set the element as selected...
+ State.state.selectedElement.setData(feature);
- layer.bindPopup(popup);
- const eventSource = State.state.allElements.addOrGetElement(feature);
- const uiElement = self._showOnPopup(eventSource, feature);
- // We first render the UIelement (which'll still need an update later on...)
- // But at least it'll be visible already
- popup.setContent(uiElement.Render());
- popup.openOn(State.state.bm.map);
- // popup.openOn(State.state.bm.map);
- // ANd we perform the pending update
- uiElement.Update();
}
- // We set the element as selected...
- State.state.selectedElement.setData(feature);
- // We mark the event as consumed
- L.DomEvent.stop(e);
- });
- }
- }
- )
- ;
+ layer.on("click", (e) => {
+ // @ts-ignore
+ openPopup(e.latlng);
+ // We mark the event as consumed
+ L.DomEvent.stop(e);
+ });
+
+ if (feature.properties.id.replace(/\//g, "_") === Hash.Get().data) {
+ const center = GeoOperations.centerpoint(feature).geometry.coordinates;
+ openPopup({lat: center[1], lng: center[0]})
+ }
+
+ }
+ });
if (this.combinedIsDisplayed.data) {
this._geolayer.addTo(State.state.bm.map);
diff --git a/Logic/Leaflet/GeoLocationHandler.ts b/Logic/Leaflet/GeoLocationHandler.ts
index 5975529e4..e94b23e3d 100644
--- a/Logic/Leaflet/GeoLocationHandler.ts
+++ b/Logic/Leaflet/GeoLocationHandler.ts
@@ -4,7 +4,6 @@ import {UIElement} from "../../UI/UIElement";
import State from "../../State";
import {Utils} from "../../Utils";
import {Basemap} from "./Basemap";
-import {FixedUiElement} from "../../UI/Base/FixedUiElement";
import Svg from "../../Svg";
import {Img} from "../../UI/Img";
@@ -48,15 +47,18 @@ export class GeoLocationHandler extends UIElement {
map.on('accuratepositionfound', onAccuratePositionFound);
map.on('accuratepositionerror', onAccuratePositionError);
-FixedUiElement
- const icon = L.icon(
- {
- iconUrl: Img.AsData(Svg.crosshair_blue),
- iconSize: [40, 40], // size of the icon
- iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
- })
+
State.state.currentGPSLocation.addCallback((location) => {
+
+ const color = getComputedStyle(document.body).getPropertyValue("--catch-detail-color")
+ const icon = L.icon(
+ {
+ iconUrl: Img.AsData(Svg.crosshair.replace(/#000000/g, color)),
+ iconSize: [40, 40], // size of the icon
+ iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
+ })
+
const newMarker = L.marker(location.latlng, {icon: icon});
newMarker.addTo(map);
diff --git a/Logic/UpdateFromOverpass.ts b/Logic/UpdateFromOverpass.ts
index 8cd6bbec3..bbf83c4b0 100644
--- a/Logic/UpdateFromOverpass.ts
+++ b/Logic/UpdateFromOverpass.ts
@@ -62,6 +62,11 @@ export class UpdateFromOverpass {
if (state.locationControl.data.zoom < layer.minzoom) {
continue;
}
+ if(layer.doNotDownload){
+ continue;
+ }
+
+
// Check if data for this layer has already been loaded
let previouslyLoaded = false;
for (let z = layer.minzoom; z < 25 && !previouslyLoaded; z++) {
diff --git a/Logic/Web/Hash.ts b/Logic/Web/Hash.ts
new file mode 100644
index 000000000..6ca2d99e5
--- /dev/null
+++ b/Logic/Web/Hash.ts
@@ -0,0 +1,18 @@
+import {UIEventSource} from "../UIEventSource";
+
+export default class Hash {
+
+ public static Get() : UIEventSource{
+ const hash = new UIEventSource(window.location.hash.substr(1));
+ hash.addCallback(h => {
+ h = h.replace(/\//g, "_");
+ return window.location.hash = "#" + h;
+ });
+ window.onhashchange = () => {
+ hash.setData(window.location.hash.substr(1))
+ }
+
+ return hash;
+ }
+
+}
\ No newline at end of file
diff --git a/Logic/Web/QueryParameters.ts b/Logic/Web/QueryParameters.ts
index 821c163c2..dd8456e37 100644
--- a/Logic/Web/QueryParameters.ts
+++ b/Logic/Web/QueryParameters.ts
@@ -2,6 +2,7 @@
* Wraps the query parameters into UIEventSources
*/
import {UIEventSource} from "../UIEventSource";
+import Hash from "./Hash";
export class QueryParameters {
@@ -57,7 +58,7 @@ export class QueryParameters {
parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(QueryParameters.knownSources[key].data))
}
- history.replaceState(null, "", "?" + parts.join("&"));
+ history.replaceState(null, "", "?" + parts.join("&") + "#" + Hash.Get().data);
}
diff --git a/State.ts b/State.ts
index f9772fe22..a1921d1ee 100644
--- a/State.ts
+++ b/State.ts
@@ -12,6 +12,7 @@ import {LocalStorageSource} from "./Logic/Web/LocalStorageSource";
import {QueryParameters} from "./Logic/Web/QueryParameters";
import {BaseLayer} from "./Logic/BaseLayer";
import LayoutConfig from "./Customizations/JSON/LayoutConfig";
+import Hash from "./Logic/Web/Hash";
/**
* Contains the global state: a bunch of UI-event sources
@@ -21,9 +22,9 @@ export default class State {
// The singleton of the global state
public static state: State;
-
- public static vNumber = "0.1.3-rc2+g";
-
+
+ public static vNumber = "0.1.3-rc4";
+
// The user journey states thresholds when a new feature gets unlocked
public static userJourney = {
addNewPointsUnlock: 0,
@@ -209,6 +210,22 @@ export default class State {
);
+ const h = Hash.Get();
+ this.selectedElement.addCallback(selected => {
+ if (selected === undefined) {
+ h.setData("");
+ } else {
+ h.setData(selected.id)
+ }
+ }
+ )
+ h.addCallbackAndRun(hash => {
+ if(hash === undefined || hash === ""){
+ self.selectedElement.setData(undefined);
+ }
+ })
+
+
this.installedThemes = this.osmConnection.preferencesHandler.preferences.map<{ layout: LayoutConfig, definition: string }[]>(allPreferences => {
const installedThemes: { layout: LayoutConfig, definition: string }[] = [];
if (allPreferences === undefined) {
diff --git a/Svg.ts b/Svg.ts
index 190761062..e1a6704f4 100644
--- a/Svg.ts
+++ b/Svg.ts
@@ -79,6 +79,11 @@ export default class Svg {
public static direction_svg() { return new FixedUiElement(Svg.direction);}
public static direction_ui() { return new FixedUiElement(Svg.direction_img);}
+ public static direction_gradient = " "
+ public static direction_gradient_img = Img.AsImageElement(Svg.direction_gradient)
+ public static direction_gradient_svg() { return new FixedUiElement(Svg.direction_gradient);}
+ public static direction_gradient_ui() { return new FixedUiElement(Svg.direction_gradient_img);}
+
public static down = " "
public static down_img = Img.AsImageElement(Svg.down)
public static down_svg() { return new FixedUiElement(Svg.down);}
@@ -214,4 +219,4 @@ export default class Svg {
public static wikipedia_svg() { return new FixedUiElement(Svg.wikipedia);}
public static wikipedia_ui() { return new FixedUiElement(Svg.wikipedia_img);}
-}
+public static All = {"add.svg": Svg.add,"addSmall.svg": Svg.addSmall,"ampersand.svg": Svg.ampersand,"arrow-left-smooth.svg": Svg.arrow_left_smooth,"arrow-right-smooth.svg": Svg.arrow_right_smooth,"bug.svg": Svg.bug,"camera-plus.svg": Svg.camera_plus,"checkmark.svg": Svg.checkmark,"close.svg": Svg.close,"compass.svg": Svg.compass,"crosshair-blue-center.svg": Svg.crosshair_blue_center,"crosshair-blue.svg": Svg.crosshair_blue,"crosshair.svg": Svg.crosshair,"delete_icon.svg": Svg.delete_icon,"direction.svg": Svg.direction,"direction_gradient.svg": Svg.direction_gradient,"down.svg": Svg.down,"envelope.svg": Svg.envelope,"floppy.svg": Svg.floppy,"gear.svg": Svg.gear,"help.svg": Svg.help,"home.svg": Svg.home,"home_white_bg.svg": Svg.home_white_bg,"josm_logo.svg": Svg.josm_logo,"layers.svg": Svg.layers,"layersAdd.svg": Svg.layersAdd,"logo.svg": Svg.logo,"logout.svg": Svg.logout,"mapillary.svg": Svg.mapillary,"no_checkmark.svg": Svg.no_checkmark,"or.svg": Svg.or,"osm-logo-us.svg": Svg.osm_logo_us,"osm-logo.svg": Svg.osm_logo,"pencil.svg": Svg.pencil,"pop-out.svg": Svg.pop_out,"reload.svg": Svg.reload,"search.svg": Svg.search,"share.svg": Svg.share,"star.svg": Svg.star,"statistics.svg": Svg.statistics,"up.svg": Svg.up,"wikimedia-commons-white.svg": Svg.wikimedia_commons_white,"wikipedia.svg": Svg.wikipedia};}
diff --git a/UI/FullScreenMessageBoxHandler.ts b/UI/FullScreenMessageBoxHandler.ts
index d1e935556..70d5d907a 100644
--- a/UI/FullScreenMessageBoxHandler.ts
+++ b/UI/FullScreenMessageBoxHandler.ts
@@ -14,7 +14,6 @@ export class FullScreenMessageBox extends UIElement {
constructor(onClear: (() => void)) {
super(State.state.fullScreenMessage);
this.HideOnEmpty(true);
- const self = this;
this.returnToTheMap =
new Combine([Translations.t.general.returnToTheMap.Clone().SetStyle("font-size:xx-large")])
diff --git a/UI/Input/DirectionInput.ts b/UI/Input/DirectionInput.ts
index 7188ad26e..3fe834406 100644
--- a/UI/Input/DirectionInput.ts
+++ b/UI/Input/DirectionInput.ts
@@ -2,6 +2,10 @@ import {InputElement} from "./InputElement";
import {UIEventSource} from "../../Logic/UIEventSource";
import Combine from "../Base/Combine";
import Svg from "../../Svg";
+import * as L from "leaflet"
+import * as X from "leaflet-providers"
+import {Basemap} from "../../Logic/Leaflet/Basemap";
+import State from "../../State";
/**
* Selects a direction in degrees
@@ -34,8 +38,8 @@ export default class DirectionInput extends InputElement {
}
InnerRender(): string {
- console.log("Inner render direction")
return new Combine([
+ ``,
Svg.direction_svg().SetStyle(
`position: absolute;top: 0;left: 0;width: 100%;height: 100%;rotate:${this.value.data}deg;`)
.SetClass("direction-svg"),
@@ -47,8 +51,7 @@ export default class DirectionInput extends InputElement {
}
protected InnerUpdate(htmlElement: HTMLElement) {
- console.log("Inner update direction")
- super.InnerUpdate(htmlElement);
+ super.InnerUpdate(htmlElement);
const self = this;
function onPosChange(x: number, y: number) {
@@ -57,7 +60,7 @@ export default class DirectionInput extends InputElement {
const dy = (rect.top + rect.bottom) / 2 - y;
const angle = 180 * Math.atan2(dy, dx) / Math.PI;
const angleGeo = Math.floor((450 - angle) % 360);
- self.value.setData(""+angleGeo)
+ self.value.setData("" + angleGeo)
}
diff --git a/UI/Input/ValidatedTextField.ts b/UI/Input/ValidatedTextField.ts
index 9c9e23cf5..703ed1900 100644
--- a/UI/Input/ValidatedTextField.ts
+++ b/UI/Input/ValidatedTextField.ts
@@ -16,7 +16,9 @@ interface TextFieldDef {
explanation: string,
isValid: ((s: string, country?: string) => boolean),
reformat?: ((s: string, country?: string) => string),
- inputHelper?: (value: UIEventSource) => InputElement,
+ inputHelper?: (value: UIEventSource, options?: {
+ location: [number, number]
+ }) => InputElement,
}
export default class ValidatedTextField {
@@ -26,7 +28,9 @@ export default class ValidatedTextField {
explanation: string,
isValid?: ((s: string, country?: string) => boolean),
reformat?: ((s: string, country?: string) => string),
- inputHelper?: (value: UIEventSource) => InputElement): TextFieldDef {
+ inputHelper?: (value: UIEventSource, options?:{
+ location: [number, number]
+ }) => InputElement): TextFieldDef {
if (isValid === undefined) {
isValid = () => true;
@@ -197,7 +201,8 @@ export default class ValidatedTextField {
textArea?: boolean,
textAreaRows?: number,
isValid?: ((s: string, country: string) => boolean),
- country?: string
+ country?: string,
+ location?: [number /*lat*/, number /*lon*/]
}): InputElement {
options = options ?? {};
options.placeholder = options.placeholder ?? type;
@@ -230,7 +235,9 @@ export default class ValidatedTextField {
}
if (tp.inputHelper) {
- input = new CombinedInputElement(input, tp.inputHelper(input.GetValue()));
+ input = new CombinedInputElement(input, tp.inputHelper(input.GetValue(),{
+ location: options.location
+ }));
}
return input;
}
diff --git a/UI/Popup/FeatureInfoBox.ts b/UI/Popup/FeatureInfoBox.ts
index ed66eb2a6..71145f943 100644
--- a/UI/Popup/FeatureInfoBox.ts
+++ b/UI/Popup/FeatureInfoBox.ts
@@ -28,7 +28,8 @@ export class FeatureInfoBox extends UIElement {
this._layerConfig = layerConfig;
- this._title = new TagRenderingAnswer(tags, layerConfig.title)
+ this._title = layerConfig.title === undefined ? undefined :
+ new TagRenderingAnswer(tags, layerConfig.title)
.SetClass("featureinfobox-title");
this._titleIcons = new Combine(
layerConfig.titleIcons.map(icon => new TagRenderingAnswer(tags, icon)))
diff --git a/UI/Popup/TagRenderingAnswer.ts b/UI/Popup/TagRenderingAnswer.ts
index 9bafc24ec..d5e062d81 100644
--- a/UI/Popup/TagRenderingAnswer.ts
+++ b/UI/Popup/TagRenderingAnswer.ts
@@ -15,6 +15,9 @@ export default class TagRenderingAnswer extends UIElement {
super(tags);
this._tags = tags;
this._configuration = configuration;
+ if(configuration === undefined){
+ throw "Trying to generate a tagRenderingAnswer without configuration..."
+ }
}
InnerRender(): string {
diff --git a/UI/Popup/TagRenderingQuestion.ts b/UI/Popup/TagRenderingQuestion.ts
index d80342911..68f48f9ca 100644
--- a/UI/Popup/TagRenderingQuestion.ts
+++ b/UI/Popup/TagRenderingQuestion.ts
@@ -251,7 +251,8 @@ export default class TagRenderingQuestion extends UIElement {
const textField = ValidatedTextField.InputForType(this._configuration.freeform.type, {
isValid: (str) => (str.length <= 255),
- country: this._tags.data._country
+ country: this._tags.data._country,
+ location: [this._tags.data._lat, this._tags.data._lon]
});
textField.GetValue().setData(this._tags.data[this._configuration.freeform.key]);
diff --git a/UI/SpecialVisualizations.ts b/UI/SpecialVisualizations.ts
index 75f260ff7..8e1b31f1e 100644
--- a/UI/SpecialVisualizations.ts
+++ b/UI/SpecialVisualizations.ts
@@ -39,12 +39,16 @@ export class SubstitutedTranslation extends UIElement {
return []
}
const tags = this.tags.data;
+ txt = SubstitutedTranslation.SubstituteKeys(txt, tags);
+ return this.EvaluateSpecialComponents(txt);
+ }
+
+ public static SubstituteKeys(txt: string, tags: any) {
for (const key in tags) {
// Poor mans replace all
txt = txt.split("{" + key + "}").join(tags[key]);
}
-
- return this.EvaluateSpecialComponents(txt);
+ return txt;
}
private EvaluateSpecialComponents(template: string): UIElement[] {
diff --git a/UI/UserBadge.ts b/UI/UserBadge.ts
index f733d9baa..04ffcd9ac 100644
--- a/UI/UserBadge.ts
+++ b/UI/UserBadge.ts
@@ -94,15 +94,6 @@ export class UserBadge extends UIElement {
dryrun = new FixedUiElement("TESTING").SetClass("alert");
}
- if (user.home !== undefined) {
- const icon = L.icon({
- iconUrl: Img.AsData(Svg.home_white_bg),
- iconSize: [30, 30],
- iconAnchor: [15, 15]
- });
- L.marker([user.home.lat, user.home.lon], {icon: icon}).addTo(State.state.bm.map)
- }
-
const settings =
new Link(Svg.gear_svg(),
`https://www.openstreetmap.org/user/${encodeURIComponent(user.name)}/account`,
diff --git a/Utils.ts b/Utils.ts
index 423ddb3e1..a0b98157a 100644
--- a/Utils.ts
+++ b/Utils.ts
@@ -3,6 +3,7 @@ import * as $ from "jquery"
export class Utils {
+ public static readonly assets_path = "./assets/svg/";
static EncodeXmlValue(str) {
return str.replace(/&/g, '&')
@@ -167,4 +168,6 @@ export class Utils {
console.log("Added custom layout ",location)
}
+
+
}
diff --git a/assets/layers/direction/direction.json b/assets/layers/direction/direction.json
new file mode 100644
index 000000000..ac096e235
--- /dev/null
+++ b/assets/layers/direction/direction.json
@@ -0,0 +1,32 @@
+{
+ "id": "direction",
+ "name": {
+ "en": "Direction visualization"
+ },
+ "minzoom": 16,
+ "overpassTags": {
+ "or": ["camera:direction~*","direction~*"]
+ },
+ "doNotDownload": true,
+ "passAllFeatures": true,
+ "title": null,
+ "description": {
+ "en": "This layer visualizes directions"
+ },
+ "tagRenderings": [],
+ "icon": "./assets/svg/direction_gradient.svg",
+ "rotation": {
+ "render": "{camera:direction}",
+ "mappings": [
+ {
+ "if": "direction~*",
+ "then": "{direction}"
+ }
+ ]
+ },
+ "iconSize": "200,200,center",
+ "color": "--catch-detail-color",
+ "stroke": "0",
+ "presets": [],
+ "wayHandling": 2
+}
\ No newline at end of file
diff --git a/assets/svg/direction_gradient.svg b/assets/svg/direction_gradient.svg
new file mode 100644
index 000000000..aa274b406
--- /dev/null
+++ b/assets/svg/direction_gradient.svg
@@ -0,0 +1,54 @@
+
+
diff --git a/assets/themes/surveillance_cameras/custom_theme.css b/assets/themes/surveillance_cameras/custom_theme.css
index d68e02a28..97a49d87b 100644
--- a/assets/themes/surveillance_cameras/custom_theme.css
+++ b/assets/themes/surveillance_cameras/custom_theme.css
@@ -10,3 +10,17 @@ html {
--shadow-color: #0f0 !important;
}
+#innercolor {
+ stop-color:#ff0000
+}
+.leaflet-div-icon svg {
+ width: calc(100% - 3px);
+ height: calc(100% + 3px);
+}
+/*
+.leaflet-div-icon svg path {
+ fill: none !important;
+ stroke-width: 1px !important;
+ stroke: #0f0 !important;
+}
+*/
diff --git a/assets/themes/surveillance_cameras/surveillance_cameras.json b/assets/themes/surveillance_cameras/surveillance_cameras.json
index c5021203d..3069b229f 100644
--- a/assets/themes/surveillance_cameras/surveillance_cameras.json
+++ b/assets/themes/surveillance_cameras/surveillance_cameras.json
@@ -27,6 +27,7 @@
"customCss": "./assets/themes/surveillance_cameras/custom_theme.css",
"defaultBackgroundId": "Stadia.AlidadeSmoothDark",
"layers": [
+ "direction",
{
"id": "cameras",
"name": {
@@ -56,6 +57,7 @@
"tagRenderings": [
"images",
{
+ "#": "Camera type: fixed; panning; dome",
"question": {
"en": "What kind of camera is this?",
"nl": "Wat voor soort camera is dit?"
@@ -97,18 +99,32 @@
]
},
{
+ "#": "direction. We don't ask this for a dome on a pole or ceiling as it has a 360° view",
"question": {
"en": "In which geographical direction does this camera film?",
"nl": "Naar welke geografische richting filmt deze camera?"
},
"render": "Films to {camera:direction}",
- "condition": "camera:type!=dome",
+ "condition": {
+ "not": {
+ "and": [
+ "camera:type=dome",
+ {
+ "or": [
+ "camera:mount=ceiling",
+ "camera:mount=pole"
+ ]
+ }
+ ]
+ }
+ },
"freeform": {
"key": "camera:direction",
"type": "direction"
}
},
{
+ "#": "Operator",
"freeform": {
"key": "operator"
},
@@ -122,6 +138,7 @@
}
},
{
+ "#": "Surveillance type: public, outdoor, indoor",
"question": {
"en": "What kind of surveillance is this camera",
"nl": "Wat soort bewaking wordt hier uitgevoerd?"
@@ -134,8 +151,8 @@
]
},
"then": {
- "en": "A public area is surveilled, such as a street, a bridge, a square, a park, a train station...",
- "nl": "Bewaking van de publieke ruilmte, dus een straat, een brug, een park, een plein, een stationsgebouw..."
+ "en": "A public area is surveilled, such as a street, a bridge, a square, a park, a train station, a public corridor or tunnel,...",
+ "nl": "Bewaking van de publieke ruilmte, dus een straat, een brug, een park, een plein, een stationsgebouw, een publiek toegankelijke gang of tunnel..."
}
},
{
@@ -156,13 +173,67 @@
]
},
"then": {
- "nl": "Een private binnenruimte wordt bewaakt, bv. een wiinkel, een parkeergarage, ...",
+ "nl": "Een private binnenruimte wordt bewaakt, bv. een winkel, een parkeergarage, ...",
"en": "A private indoor area is surveilled, e.g. a shop, a private underground parking, ..."
}
}
]
},
{
+ "#": "Indoor camera? This isn't clear for 'public'-cameras",
+ "question": {
+ "en": "Is the public space surveilled by this camera an indoor or outdoor space?",
+ "nl": "Bevindt de bewaakte publieke ruimte camera zich binnen of buiten?"
+ },
+ "condition": {
+ "and": [
+ "surveillance:type=public"
+ ]
+ },
+ "mappings": [
+ {
+ "if": "indoor=yes",
+ "then": {
+ "en": "This camera is located indoors",
+ "nl": "Deze camera bevindt zich binnen"
+ }
+ },
+ {
+ "if": "indoor=no",
+ "then": {
+ "en": "This camera is located outdoors",
+ "nl": "Deze camera bevindt zich buiten"
+ }
+ },
+ {
+ "if": "indoor=",
+ "then": {
+ "en": "This camera is probably located outdoors",
+ "nl": "Deze camera bevindt zich waarschijnlijk buiten"
+ },
+ "hideInAnswer": true
+ }
+ ]
+ },
+ {
+ "#": "Level",
+ "question": {
+ "en": "On which level is this camera located?",
+ "nl": "Op welke verdieping bevindt deze camera zich?"
+ },
+ "freeform": {
+ "key": "level",
+ "type": "nat"
+ },
+ "condition": {
+ "or": [
+ "indoor=yes",
+ "surveillance:type=ye"
+ ]
+ }
+ },
+ {
+ "#": "Surveillance:zone",
"question": {
"en": "What exactly is surveilled here?",
"nl": "Wat wordt hier precies bewaakt?"
@@ -244,6 +315,7 @@
]
},
{
+ "#": "camera:mount",
"question": {
"en": "How is this camera placed?",
"nl": "Hoe is deze camera geplaatst?"
@@ -267,10 +339,10 @@
}
},
{
- "if": "camera:mount=pole",
+ "if": "camera:mount=ceiling",
"then": {
- "en": "This camera is placed one a pole",
- "nl": "Deze camera staat op een paal"
+ "en": "This camera is placed on the ceiling",
+ "nl": "Deze camera hangt aan het plafond"
}
}
]
@@ -293,7 +365,7 @@
"render": "50,50,center"
},
"color": {
- "render": "#00f"
+ "render": "#f00"
},
"presets": [
{
diff --git a/css/mobile.css b/css/mobile.css
index 6b1cde23d..8525b1310 100644
--- a/css/mobile.css
+++ b/css/mobile.css
@@ -45,10 +45,6 @@ Contains tweaks for small screens
}
.leaflet-popup {
- transform: unset !important;
- }
-
- .leaflet-popup-content {
/* On mobile, the popups are shown as a full-screen element */
display: none;
visibility: hidden;
diff --git a/css/tagrendering.css b/css/tagrendering.css
index 769468743..aa82b6948 100644
--- a/css/tagrendering.css
+++ b/css/tagrendering.css
@@ -38,6 +38,11 @@
}
+.question svg {
+ width: 100%;
+ height: 100%;
+}
+
.question-text {
font-size: larger;
font-weight: bold;
diff --git a/index.css b/index.css
index 4fbd1eedd..04b638243 100644
--- a/index.css
+++ b/index.css
@@ -255,7 +255,6 @@ a {
position: absolute;
z-index: 5000;
transition: all 500ms linear;
- overflow-x: hidden;
pointer-events: none;
/* Shadow offset */
padding: 0.5em 10px 0 0.5em;
@@ -410,6 +409,12 @@ a {
overflow-y: auto;
overflow-x: hidden;
}
+
+.leaflet-div-icon {
+ background-color: unset !important;
+ border: unset !important;
+}
+
/****** ShareScreen *****/
.literal-code {
diff --git a/install.bat b/install.bat
deleted file mode 100644
index b66c116ad..000000000
--- a/install.bat
+++ /dev/null
@@ -1 +0,0 @@
-npm install
\ No newline at end of file
diff --git a/package.json b/package.json
index a354cbc1b..5d40ea91f 100644
--- a/package.json
+++ b/package.json
@@ -16,10 +16,11 @@
"start": "parcel *.html UI/** Logic/** assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*",
"test": "ts-node test/*",
"generate:editor-layer-index": "cd assets/ && wget https://osmlab.github.io/editor-layer-index/imagery.geojson --output-document=editor-layer-index.json",
- "generate:images": "ts-node generateIncludedImages.ts",
- "generate:layouts": "ts-node createLayouts.ts",
+ "generate:images": "ts-node scripts/generateIncludedImages.ts",
+ "generate:translations": "ts-node scripts/generateTranslations.ts",
+ "generate:layouts": "ts-node scripts/createLayouts.ts",
"optimize-images": "cd assets/generated/ && find -name '*.png' -exec optipng '{}' \\; && echo 'PNGs are optimized'",
- "generate": "npm run generate:images && npm run generate:layouts && npm run generate:editor-layer-index",
+ "generate": "npm run generate:images && npm run generate:translations && npm run generate:layouts && npm run generate:editor-layer-index",
"build": "rm -rf dist/ npm run generate && parcel build --public-url ./ *.html assets/** assets/**/** assets/**/**/** vendor/* vendor/*/*",
"prepare-deploy": "npm run generate && npm run build && rm -rf .cache",
"deploy:staging": "npm run prepare-deploy && rm -rf /home/pietervdvn/git/pietervdvn.github.io/Staging/* && cp -r dist/* /home/pietervdvn/git/pietervdvn.github.io/Staging/ && cd /home/pietervdvn/git/pietervdvn.github.io/ && git add * && git commit -m 'New MapComplete Version' && git push && cd - && npm run clean",
diff --git a/createLayouts.ts b/scripts/createLayouts.ts
similarity index 92%
rename from createLayouts.ts
rename to scripts/createLayouts.ts
index 2b7c380d9..e596c09a1 100644
--- a/createLayouts.ts
+++ b/scripts/createLayouts.ts
@@ -1,16 +1,16 @@
-import {Img} from "./UI/Img"
-import {UIElement} from "./UI/UIElement";
+import {Img} from "../UI/Img"
+import {UIElement} from "../UI/UIElement";
Img.runningFromConsole = true;
// We HAVE to mark this while importing
UIElement.runningFromConsole = true;
-import {AllKnownLayouts} from "./Customizations/AllKnownLayouts";
+import {AllKnownLayouts} from "../Customizations/AllKnownLayouts";
import {existsSync, mkdirSync, readFileSync, writeFile, writeFileSync} from "fs";
-import Locale from "./UI/i18n/Locale";
+import Locale from "../UI/i18n/Locale";
import svg2img from 'promise-svg2img';
-import Translations from "./UI/i18n/Translations";
-import {Translation} from "./UI/i18n/Translation";
-import LayoutConfig from "./Customizations/JSON/LayoutConfig";
+import Translations from "../UI/i18n/Translations";
+import {Translation} from "../UI/i18n/Translation";
+import LayoutConfig from "../Customizations/JSON/LayoutConfig";
function enc(str: string): string {
@@ -100,7 +100,7 @@ const alreadyWritten = []
function createIcon(iconPath: string, size: number, layout: LayoutConfig) {
let name = iconPath.split(".").slice(0, -1).join(".");
- if (name.startsWith("./")) {
+ if (name.startsWith("../")) {
name = name.substr(2)
}
const newname = `${name}${size}.png`
@@ -151,7 +151,7 @@ function createManifest(layout: LayoutConfig, relativePath: string) {
let path = layout.icon;
if (layout.icon.startsWith("<")) {
// THis is already the svg
- path = "./assets/generated/" + layout.id + "_logo.svg"
+ path = "../assets/generated/" + layout.id + "_logo.svg"
writeFileSync(path, layout.icon)
}
@@ -212,19 +212,19 @@ function createLandingPage(layout: LayoutConfig) {
}
const og = `
-
+
`
let icon = layout.icon;
if (icon.startsWith("", og)
.replace(/.+?<\/title>/, `${ogTitle}`)
.replace("Loading MapComplete, hang on...", `Loading MapComplete theme ${ogTitle}...`)
@@ -251,7 +251,7 @@ let wikiPage = "{|class=\"wikitable sortable\"\n" +
"|-";
-const generatedDir = "./assets/generated";
+const generatedDir = "../assets/generated";
if (! existsSync(generatedDir)) {
mkdirSync(generatedDir)
}
diff --git a/generateIncludedImages.ts b/scripts/generateIncludedImages.ts
similarity index 55%
rename from generateIncludedImages.ts
rename to scripts/generateIncludedImages.ts
index 73a8251b9..75261b2e3 100644
--- a/generateIncludedImages.ts
+++ b/scripts/generateIncludedImages.ts
@@ -1,5 +1,5 @@
import * as fs from "fs";
-import {Utils} from "./Utils";
+import {Utils} from "../Utils";
function genImages() {
@@ -7,6 +7,7 @@ function genImages() {
const dir = fs.readdirSync("./assets/svg")
let module = "import {Img} from \"./UI/Img\";\nimport {FixedUiElement} from \"./UI/Base/FixedUiElement\";\n\nexport default class Svg {\n\n\n";
+ const allNames: string[] = [];
for (const path of dir) {
if (!path.endsWith(".svg")) {
@@ -26,47 +27,11 @@ function genImages() {
module += ` public static ${name}_img = Img.AsImageElement(Svg.${name})\n`
module += ` public static ${name}_svg() { return new FixedUiElement(Svg.${name});}\n`
module += ` public static ${name}_ui() { return new FixedUiElement(Svg.${name}_img);}\n\n`
+ allNames.push(`"${path}": Svg.${name}`)
}
+ module += `public static All = {${allNames.join(",")}};`
module += "}\n";
fs.writeFileSync("Svg.ts", module);
console.log("Done")
}
-
-function isTranslation(tr: any): boolean {
- for (const key in tr) {
- if (typeof tr[key] !== "string") {
- return false;
- }
- }
- return true;
-}
-
-function transformTranslation(obj: any, depth = 1) {
-
- if (isTranslation(obj)) {
- return `new Translation( ${JSON.stringify(obj)} )`
- }
-
- let values = ""
- for (const key in obj) {
- values += (Utils.Times((_) => " ", depth)) + key + ": " + transformTranslation(obj[key], depth + 1) + ",\n"
- }
- return `{${values}}`;
-
-}
-
-function genTranslations() {
- const translations = JSON.parse(fs.readFileSync("./assets/translations.json", "utf-8"))
- const transformed = transformTranslation(translations);
-
- let module = `import {Translation} from "./UI/i18n/Translation"\n\nexport default class AllTranslationAssets {\n\n`;
- module += " public static t = " + transformed;
- module += "}"
-
- fs.writeFileSync("AllTranslationAssets.ts", module);
-
-
-}
-
-genTranslations()
genImages()
\ No newline at end of file
diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts
new file mode 100644
index 000000000..590cd084e
--- /dev/null
+++ b/scripts/generateTranslations.ts
@@ -0,0 +1,40 @@
+import * as fs from "fs";
+import {Utils} from "../Utils";
+
+function isTranslation(tr: any): boolean {
+ for (const key in tr) {
+ if (typeof tr[key] !== "string") {
+ return false;
+ }
+ }
+ return true;
+}
+
+function transformTranslation(obj: any, depth = 1) {
+
+ if (isTranslation(obj)) {
+ return `new Translation( ${JSON.stringify(obj)} )`
+ }
+
+ let values = ""
+ for (const key in obj) {
+ values += (Utils.Times((_) => " ", depth)) + key + ": " + transformTranslation(obj[key], depth + 1) + ",\n"
+ }
+ return `{${values}}`;
+
+}
+
+function genTranslations() {
+ const translations = JSON.parse(fs.readFileSync("./assets/translations.json", "utf-8"))
+ const transformed = transformTranslation(translations);
+
+ let module = `import {Translation} from "./UI/i18n/Translation"\n\nexport default class AllTranslationAssets {\n\n`;
+ module += " public static t = " + transformed;
+ module += "}"
+
+ fs.writeFileSync("AllTranslationAssets.ts", module);
+
+
+}
+
+genTranslations()
\ No newline at end of file
diff --git a/test.html b/test.html
index fe0a77720..521de2287 100644
--- a/test.html
+++ b/test.html
@@ -25,7 +25,10 @@
-'maindiv' not attached
+
+
+
'maindiv' not attached
+
diff --git a/test.ts b/test.ts
index 87cb971bd..1a50ef30d 100644
--- a/test.ts
+++ b/test.ts
@@ -6,7 +6,7 @@ import {UIEventSource} from "./Logic/UIEventSource";
import {VariableUiElement} from "./UI/Base/VariableUIElement";
const d = new UIEventSource("90");
-new Direction(d).AttachTo("maindiv")
+new Direction(d, [51.21576,3.22001]).AttachTo("maindiv")
new VariableUiElement(d.map(d => "" + d + "°")).AttachTo("extradiv")
UIEventSource.Chronic(25, () => {