| 
									
										
										
										
											2023-10-10 01:52:02 +02:00
										 |  |  | import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFile, writeFileSync } from "fs" | 
					
						
							|  |  |  | import Locale from "../src/UI/i18n/Locale" | 
					
						
							|  |  |  | import Translations from "../src/UI/i18n/Translations" | 
					
						
							|  |  |  | import { Translation } from "../src/UI/i18n/Translation" | 
					
						
							|  |  |  | import all_known_layouts from "../src/assets/generated/known_themes.json" | 
					
						
							|  |  |  | import { LayoutConfigJson } from "../src/Models/ThemeConfig/Json/LayoutConfigJson" | 
					
						
							|  |  |  | import LayoutConfig from "../src/Models/ThemeConfig/LayoutConfig" | 
					
						
							|  |  |  | import xml2js from "xml2js" | 
					
						
							|  |  |  | import ScriptUtils from "./ScriptUtils" | 
					
						
							|  |  |  | import { Utils } from "../src/Utils" | 
					
						
							|  |  |  | import SpecialVisualizations from "../src/UI/SpecialVisualizations" | 
					
						
							|  |  |  | import Constants from "../src/Models/Constants" | 
					
						
							|  |  |  | import { AvailableRasterLayers, RasterLayerPolygon } from "../src/Models/RasterLayers" | 
					
						
							|  |  |  | import { ImmutableStore } from "../src/Logic/UIEventSource" | 
					
						
							|  |  |  | import * as crypto from "crypto" | 
					
						
							|  |  |  | import * as eli from "../src/assets/editor-layer-index.json" | 
					
						
							|  |  |  | import * as eli_global from "../src/assets/global-raster-layers.json" | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  | import ValidationUtils from "../src/Models/ThemeConfig/Conversion/ValidationUtils" | 
					
						
							| 
									
										
										
										
											2023-10-08 17:38:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | const sharp = require("sharp") | 
					
						
							|  |  |  | const template = readFileSync("theme.html", "utf8") | 
					
						
							| 
									
										
										
										
											2023-12-06 12:12:53 +01:00
										 |  |  | let codeTemplate = readFileSync("src/index_theme.ts.template", "utf8") | 
					
						
							| 
									
										
										
										
											2021-01-06 02:21:50 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | function enc(str: string): string { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     return encodeURIComponent(str.toLowerCase()) | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  | async function createIcon(iconPath: string, size: number, alreadyWritten: string[]) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     let name = iconPath.split(".").slice(0, -1).join(".") // drop svg suffix
 | 
					
						
							| 
									
										
										
										
											2020-11-17 16:29:51 +01:00
										 |  |  |     if (name.startsWith("./")) { | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |         name = name.substr(2) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-12 18:32:10 +01:00
										 |  |  |     const newname = `assets/generated/images/${name.replace(/\//g, "_")}${size}.png` | 
					
						
							|  |  |  |     const targetpath = `public/${newname}` | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     if (alreadyWritten.indexOf(newname) >= 0) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         return newname | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     alreadyWritten.push(newname) | 
					
						
							| 
									
										
										
										
											2023-04-13 23:40:28 +02:00
										 |  |  |     if (existsSync(targetpath)) { | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         return newname | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!existsSync(iconPath)) { | 
					
						
							|  |  |  |         throw "No file at " + iconPath | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 23:37:59 +02:00
										 |  |  |     try { | 
					
						
							| 
									
										
										
										
											2020-09-30 23:34:44 +02:00
										 |  |  |         // We already read to file, in order to crash here if the file is not found
 | 
					
						
							| 
									
										
										
										
											2021-01-18 03:25:15 +01:00
										 |  |  |         let img = await sharp(iconPath) | 
					
						
							|  |  |  |         let resized = await img.resize(size) | 
					
						
							| 
									
										
										
										
											2023-02-12 18:32:10 +01:00
										 |  |  |         await resized.toFile(targetpath) | 
					
						
							| 
									
										
										
										
											2022-03-10 16:26:25 +01:00
										 |  |  |         console.log("Created png version at ", newname) | 
					
						
							| 
									
										
										
										
											2020-09-25 23:37:59 +02:00
										 |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         console.error("Could not read icon", iconPath, " to create a PNG due to", e) | 
					
						
							| 
									
										
										
										
											2020-09-20 20:28:35 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-09-25 23:37:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     return newname | 
					
						
							| 
									
										
										
										
											2020-07-25 18:00:08 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> { | 
					
						
							|  |  |  |     if (!layout.icon.endsWith(".svg")) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         console.warn( | 
					
						
							|  |  |  |             "Not creating a social image for " + | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |                 layout.id + | 
					
						
							|  |  |  |                 " as it is _not_ a .svg: " + | 
					
						
							|  |  |  |                 layout.icon | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         return undefined | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-02-09 16:19:08 +01:00
										 |  |  |     const path = `./public/assets/generated/images/social_image_${layout.id}_${template}.svg` | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     if (existsSync(path)) { | 
					
						
							|  |  |  |         return path | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     const svg = await ScriptUtils.ReadSvg(layout.icon) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     let width: string = svg.$.width | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     if (width === undefined) { | 
					
						
							|  |  |  |         throw "The logo at " + layout.icon + " does not have a defined width" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (width?.endsWith("px")) { | 
					
						
							|  |  |  |         width = width.substring(0, width.length - 2) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (width?.endsWith("%")) { | 
					
						
							|  |  |  |         throw "The logo at " + layout.icon + " has a relative width; this is not supported" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     delete svg["defs"] | 
					
						
							|  |  |  |     delete svg["$"] | 
					
						
							| 
									
										
										
										
											2023-07-16 03:26:43 +02:00
										 |  |  |     let templateSvg = await ScriptUtils.ReadSvg( | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         "./public/assets/SocialImageTemplate" + template + ".svg" | 
					
						
							| 
									
										
										
										
											2023-07-16 03:26:43 +02:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     templateSvg = Utils.WalkJson( | 
					
						
							|  |  |  |         templateSvg, | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         (leaf) => { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             const { cx, cy, r } = leaf["circle"][0].$ | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |             return { | 
					
						
							|  |  |  |                 $: { | 
					
						
							|  |  |  |                     id: "icon", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                     transform: `translate(${cx - r},${cy - r}) scale(${(r * 2) / Number(width)}) `, | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |                 }, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 g: [svg], | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         (mightBeTokenToReplace) => { | 
					
						
							|  |  |  |             if (mightBeTokenToReplace?.circle === undefined) { | 
					
						
							|  |  |  |                 return false | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return mightBeTokenToReplace.circle[0]?.$?.style?.indexOf("fill:#ff00ff") >= 0 | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const builder = new xml2js.Builder() | 
					
						
							|  |  |  |     const xml = builder.buildObject({ svg: templateSvg }) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     writeFileSync(path, xml) | 
					
						
							| 
									
										
										
										
											2022-03-10 16:26:25 +01:00
										 |  |  |     console.log("Created social image at ", path) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     return path | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | async function createManifest( | 
					
						
							|  |  |  |     layout: LayoutConfig, | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |     alreadyWritten: string[] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | ): Promise<{ | 
					
						
							|  |  |  |     manifest: any | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     whiteIcons: string[] | 
					
						
							|  |  |  | }> { | 
					
						
							| 
									
										
										
										
											2021-03-16 20:03:19 +01:00
										 |  |  |     Translation.forcedLanguage = "en" | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const icons = [] | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     const whiteIcons: string[] = [] | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     let icon = layout.icon | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |     if (icon.endsWith(".svg") || icon.startsWith("<svg") || icon.startsWith("<?xml")) { | 
					
						
							| 
									
										
										
										
											2022-02-04 00:42:02 +01:00
										 |  |  |         // This is an svg. Lets create the needed pngs and do some checkes!
 | 
					
						
							| 
									
										
										
										
											2021-01-17 21:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const whiteBackgroundPath = | 
					
						
							| 
									
										
										
										
											2023-02-09 16:19:08 +01:00
										 |  |  |             "./public/assets/generated/images/theme_" + layout.id + "_white_background.svg" | 
					
						
							| 
									
										
										
										
											2022-02-04 00:42:02 +01:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2022-02-10 23:10:39 +01:00
										 |  |  |             const svg = await ScriptUtils.ReadSvg(icon) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             const width: string = svg.$.width | 
					
						
							|  |  |  |             const height: string = svg.$.height | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             const builder = new xml2js.Builder() | 
					
						
							|  |  |  |             const withRect = { rect: { $: { width, height, style: "fill:#ffffff;" } }, ...svg } | 
					
						
							|  |  |  |             const xml = builder.buildObject({ svg: withRect }) | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |             writeFileSync(whiteBackgroundPath, xml) | 
					
						
							| 
									
										
										
										
											2022-02-04 00:42:02 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         let path = layout.icon | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |         if (layout.icon.startsWith("<")) { | 
					
						
							|  |  |  |             // THis is already the svg
 | 
					
						
							| 
									
										
										
										
											2023-02-09 16:19:08 +01:00
										 |  |  |             path = "./public/assets/generated/images/" + layout.id + "_logo.svg" | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |             writeFileSync(path, layout.icon) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-01-17 21:04:37 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const sizes = [72, 96, 120, 128, 144, 152, 180, 192, 384, 512] | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |         for (const size of sizes) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             const name = await createIcon(path, size, alreadyWritten) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |             const whiteIcon = await createIcon(whiteBackgroundPath, size, alreadyWritten) | 
					
						
							|  |  |  |             whiteIcons.push(whiteIcon) | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |             icons.push({ | 
					
						
							| 
									
										
										
										
											2022-02-06 03:45:32 +01:00
										 |  |  |                 src: name, | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |                 sizes: size + "x" + size, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 type: "image/png", | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |             }) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         icons.push({ | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |             src: path, | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |             sizes: "513x513", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             type: "image/svg", | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |     } else if (icon.endsWith(".png")) { | 
					
						
							| 
									
										
										
										
											2021-07-06 15:43:21 +02:00
										 |  |  |         icons.push({ | 
					
						
							|  |  |  |             src: icon, | 
					
						
							|  |  |  |             sizes: "513x513", | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             type: "image/png", | 
					
						
							| 
									
										
										
										
											2021-07-06 15:43:21 +02:00
										 |  |  |         }) | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |         console.log(icon) | 
					
						
							| 
									
										
										
										
											2020-09-03 00:00:37 +02:00
										 |  |  |         throw "Icon is not an svg for " + layout.id | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const ogTitle = Translations.T(layout.title).txt | 
					
						
							|  |  |  |     const ogDescr = Translations.T(layout.description ?? "").txt | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     const manifest = { | 
					
						
							| 
									
										
										
										
											2022-07-12 14:22:14 +02:00
										 |  |  |         name: ogTitle, | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |         short_name: ogTitle, | 
					
						
							| 
									
										
										
										
											2021-05-10 23:43:30 +02:00
										 |  |  |         start_url: `${layout.id.toLowerCase()}.html`, | 
					
						
							| 
									
										
										
										
											2022-01-27 02:10:28 +01:00
										 |  |  |         lang: "en", | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |         display: "standalone", | 
					
						
							|  |  |  |         background_color: "#fff", | 
					
						
							|  |  |  |         description: ogDescr, | 
					
						
							|  |  |  |         orientation: "portrait-primary, landscape-primary", | 
					
						
							| 
									
										
										
										
											2022-01-06 21:05:52 +01:00
										 |  |  |         icons: icons, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         categories: ["map", "navigation"], | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     return { | 
					
						
							|  |  |  |         manifest, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         whiteIcons, | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-13 23:40:28 +02:00
										 |  |  | function asLangSpan(t: Translation, tag = "span"): string { | 
					
						
							|  |  |  |     const values: string[] = [] | 
					
						
							|  |  |  |     for (const lang in t.translations) { | 
					
						
							|  |  |  |         if (lang === "_context") { | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |         values.push(`<${tag} lang="${lang}">${t.translations[lang]}</${tag}>`) | 
					
						
							| 
									
										
										
										
											2023-04-13 23:40:28 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     return values.join("\n") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  | let previousSrc: Set<string> = new Set<string>() | 
					
						
							| 
									
										
										
										
											2023-09-29 11:13:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-06 03:34:26 +02:00
										 |  |  | let eliUrlsCached: string[] | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | async function eliUrls(): Promise<string[]> { | 
					
						
							| 
									
										
										
										
											2023-10-06 03:34:26 +02:00
										 |  |  |     if (eliUrlsCached) { | 
					
						
							| 
									
										
										
										
											2023-10-02 00:15:49 +02:00
										 |  |  |         return eliUrlsCached | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const urls: string[] = [] | 
					
						
							| 
									
										
										
										
											2023-10-06 03:34:26 +02:00
										 |  |  |     const regex = /{switch:([^}]+)}/ | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |     const rasterLayers = [ | 
					
						
							|  |  |  |         AvailableRasterLayers.maptilerDefaultLayer, | 
					
						
							|  |  |  |         ...eli.features, | 
					
						
							|  |  |  |         ...eli_global.layers.map((properties) => ({ properties })), | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2023-10-10 01:52:02 +02:00
										 |  |  |     for (const feature of rasterLayers) { | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  |         const f = <RasterLayerPolygon>feature | 
					
						
							|  |  |  |         const url = f.properties.url | 
					
						
							| 
									
										
										
										
											2023-10-02 00:15:49 +02:00
										 |  |  |         const match = url.match(regex) | 
					
						
							| 
									
										
										
										
											2023-10-06 03:34:26 +02:00
										 |  |  |         if (match) { | 
					
						
							| 
									
										
										
										
											2023-10-02 00:15:49 +02:00
										 |  |  |             const domains = match[1].split(",") | 
					
						
							|  |  |  |             const subpart = match[0] | 
					
						
							| 
									
										
										
										
											2023-10-06 03:34:26 +02:00
										 |  |  |             urls.push(...domains.map((d) => url.replace(subpart, d))) | 
					
						
							|  |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2023-10-02 00:15:49 +02:00
										 |  |  |             urls.push(url) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (f.properties.type === "vector") { | 
					
						
							|  |  |  |             // We also need to whitelist eventual sources
 | 
					
						
							|  |  |  |             const styleSpec = await Utils.downloadJsonCached(f.properties.url, 1000 * 120) | 
					
						
							| 
									
										
										
										
											2023-10-12 00:43:42 +02:00
										 |  |  |             for (const key of Object.keys(styleSpec.sources)) { | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  |                 const url = styleSpec.sources[key].url | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |                 if (!url) { | 
					
						
							| 
									
										
										
										
											2023-10-12 00:43:42 +02:00
										 |  |  |                     continue | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 let urlClipped = url | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |                 if (url.indexOf("?") > 0) { | 
					
						
							| 
									
										
										
										
											2023-10-12 00:43:42 +02:00
										 |  |  |                     urlClipped = url?.substring(0, url.indexOf("?")) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |                 console.log("Source url ", key, url) | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  |                 urls.push(url) | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |                 if (urlClipped.endsWith(".json")) { | 
					
						
							|  |  |  |                     const tileInfo = await Utils.downloadJsonCached(url, 1000 * 120) | 
					
						
							| 
									
										
										
										
											2023-10-12 00:43:42 +02:00
										 |  |  |                     urls.push(tileInfo["tiles"] ?? []) | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  |             } | 
					
						
							|  |  |  |             urls.push(...(styleSpec["tiles"] ?? [])) | 
					
						
							| 
									
										
										
										
											2023-10-12 00:43:42 +02:00
										 |  |  |             urls.push(styleSpec["sprite"]) | 
					
						
							|  |  |  |             urls.push(styleSpec["glyphs"]) | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-10-02 00:15:49 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     eliUrlsCached = urls | 
					
						
							| 
									
										
										
										
											2023-10-12 00:43:42 +02:00
										 |  |  |     return Utils.NoNull(urls).sort() | 
					
						
							| 
									
										
										
										
											2023-10-02 00:15:49 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  | async function generateCsp( | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |     layout: LayoutConfig, | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  |     layoutJson: LayoutConfigJson, | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |     options: { | 
					
						
							|  |  |  |         scriptSrcs: string[] | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  | ): Promise<string> { | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |     const apiUrls: string[] = [ | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |         "'self'", | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |         ...Constants.defaultOverpassUrls, | 
					
						
							|  |  |  |         Constants.countryCoderEndpoint, | 
					
						
							| 
									
										
										
										
											2023-09-29 11:13:30 +02:00
										 |  |  |         Constants.nominatimEndpoint, | 
					
						
							| 
									
										
										
										
											2023-11-19 21:21:33 +01:00
										 |  |  |         "https://www.openstreetmap.org", | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |         "https://api.openstreetmap.org", | 
					
						
							|  |  |  |         "https://pietervdvn.goatcounter.com", | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  |     ].concat(...(await eliUrls())) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-01 15:23:28 +01:00
										 |  |  |     SpecialVisualizations.specialVisualizations.forEach((sv) => { | 
					
						
							|  |  |  |         if (typeof sv.needsUrls === "function") { | 
					
						
							| 
									
										
										
										
											2023-11-19 22:00:37 +01:00
										 |  |  |             return | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         apiUrls.push(...sv.needsUrls) | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  |     const usedSpecialVisualisations = ValidationUtils.getSpecialVisualisationsWithArgs(layoutJson) | 
					
						
							|  |  |  |     for (const usedSpecialVisualisation of usedSpecialVisualisations) { | 
					
						
							|  |  |  |         if (typeof usedSpecialVisualisation === "string") { | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const neededUrls = usedSpecialVisualisation.func.needsUrls | 
					
						
							|  |  |  |         if (typeof neededUrls === "function") { | 
					
						
							|  |  |  |             apiUrls.push(...neededUrls(usedSpecialVisualisation.args)) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const geojsonSources: string[] = layout.layers.map((l) => l.source?.geojsonSource) | 
					
						
							|  |  |  |     const hosts = new Set<string>() | 
					
						
							|  |  |  |     const eliLayers: RasterLayerPolygon[] = AvailableRasterLayers.layersAvailableAt( | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         new ImmutableStore({ lon: 0, lat: 0 }) | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |     ).data | 
					
						
							|  |  |  |     const vectorLayers = eliLayers.filter((l) => l.properties.type === "vector") | 
					
						
							|  |  |  |     const vectorSources = vectorLayers.map((l) => l.properties.url) | 
					
						
							|  |  |  |     apiUrls.push(...vectorSources) | 
					
						
							|  |  |  |     for (const connectSource of apiUrls.concat(geojsonSources)) { | 
					
						
							|  |  |  |         if (!connectSource) { | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             const url = new URL(connectSource) | 
					
						
							|  |  |  |             hosts.add("https://" + url.host) | 
					
						
							|  |  |  |         } catch (e) { | 
					
						
							|  |  |  |             hosts.add(connectSource) | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  |     if (hosts.has("*")) { | 
					
						
							|  |  |  |         throw "* is not allowed as connect-src" | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |     const connectSrc = Array.from(hosts).sort() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const newSrcs = connectSrc.filter((newItem) => !previousSrc.has(newItem)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     console.log( | 
					
						
							|  |  |  |         "Got", | 
					
						
							|  |  |  |         hosts.size, | 
					
						
							|  |  |  |         "connect-src items for theme", | 
					
						
							|  |  |  |         layout.id, | 
					
						
							|  |  |  |         "(extra sources: ", | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         newSrcs.join(" ") + ")" | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |     ) | 
					
						
							|  |  |  |     previousSrc = hosts | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |     const csp: Record<string, string> = { | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  |         "default-src": "'self'", | 
					
						
							| 
									
										
										
										
											2023-10-20 18:23:35 +02:00
										 |  |  |         "child-src": "'self' blob: ", | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |         "img-src": "* data:", // maplibre depends on 'data:' to load
 | 
					
						
							| 
									
										
										
										
											2023-11-19 18:13:32 +01:00
										 |  |  |         "connect-src": connectSrc.join(" "), | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |         "report-to": "https://report.mapcomplete.org/csp", | 
					
						
							|  |  |  |         "worker-src": "'self' blob:", // Vite somehow loads the worker via a 'blob'
 | 
					
						
							|  |  |  |         "style-src": "'self' 'unsafe-inline'", // unsafe-inline is needed to change the default background pin colours
 | 
					
						
							| 
									
										
										
										
											2023-10-20 18:23:35 +02:00
										 |  |  |         "script-src": ["'self'", "https://gc.zgo.at/count.js", ...(options?.scriptSrcs ?? [])].join( | 
					
						
							|  |  |  |             " " | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  |     } | 
					
						
							|  |  |  |     const content = Object.keys(csp) | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |         .map((k) => k + " " + csp[k]) | 
					
						
							| 
									
										
										
										
											2023-10-20 18:23:35 +02:00
										 |  |  |         .join(" ; ") | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |     return [ | 
					
						
							|  |  |  |         `<meta http-equiv ="Report-To" content='{"group":"csp-endpoint", "max_age": 86400,"endpoints": [\{"url": "https://report.mapcomplete.org/csp"}], "include_subdomains": true}'>`, | 
					
						
							|  |  |  |         `<meta http-equiv="Content-Security-Policy" content="${content}">`, | 
					
						
							|  |  |  |     ].join("\n") | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  | const removeOtherLanguages = readFileSync("./src/UI/RemoveOtherLanguages.js", "utf8") | 
					
						
							|  |  |  |     .split("\n") | 
					
						
							|  |  |  |     .map((s) => s.trim()) | 
					
						
							|  |  |  |     .join("\n") | 
					
						
							|  |  |  | const removeOtherLanguagesHash = crypto | 
					
						
							|  |  |  |     .createHash("sha256") | 
					
						
							|  |  |  |     .update(removeOtherLanguages) | 
					
						
							|  |  |  |     .digest("base64") | 
					
						
							| 
									
										
										
										
											2023-09-29 11:13:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  | async function createLandingPage( | 
					
						
							|  |  |  |     layout: LayoutConfig, | 
					
						
							|  |  |  |     layoutJson: LayoutConfigJson, | 
					
						
							|  |  |  |     manifest, | 
					
						
							|  |  |  |     whiteIcons, | 
					
						
							|  |  |  |     alreadyWritten | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     Locale.language.setData(layout.language[0]) | 
					
						
							| 
									
										
										
										
											2022-02-06 03:45:32 +01:00
										 |  |  |     const targetLanguage = layout.language[0] | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |     const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, '\\"') | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const ogDescr = Translations.T( | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         layout.shortDescription ?? "Easily add and edit geodata with OpenStreetMap" | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     ) | 
					
						
							|  |  |  |         .textFor(targetLanguage) | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         .replace(/"/g, '\\"') | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     let ogImage = layout.socialImage | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     let twitterImage = ogImage | 
					
						
							|  |  |  |     if (ogImage === LayoutConfig.defaultSocialImage && layout.official) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ogImage = (await createSocialImage(layout, "")) ?? layout.socialImage | 
					
						
							|  |  |  |         twitterImage = (await createSocialImage(layout, "Wide")) ?? layout.socialImage | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (twitterImage.endsWith(".svg")) { | 
					
						
							|  |  |  |         // svgs are badly supported as social image, we use a generated svg instead
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         twitterImage = await createIcon(twitterImage, 512, alreadyWritten) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (ogImage.endsWith(".svg")) { | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         ogImage = await createIcon(ogImage, 512, alreadyWritten) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     let customCss = "" | 
					
						
							| 
									
										
										
										
											2020-10-23 02:15:58 +02:00
										 |  |  |     if (layout.customCss !== undefined && layout.customCss !== "") { | 
					
						
							| 
									
										
										
										
											2020-11-14 02:54:33 +01:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             const cssContent = readFileSync(layout.customCss) | 
					
						
							|  |  |  |             customCss = "<style>" + cssContent + "</style>" | 
					
						
							| 
									
										
										
										
											2020-11-14 02:54:33 +01:00
										 |  |  |         } catch (e) { | 
					
						
							|  |  |  |             customCss = `<link rel='stylesheet' href="${layout.customCss}"/>` | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-23 02:15:58 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     const og = `
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     <meta property="og:image" content="${ogImage ?? "assets/SocialImage.png"}"> | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     <meta property="og:title" content="${ogTitle}"> | 
					
						
							| 
									
										
										
										
											2022-01-18 20:18:12 +01:00
										 |  |  |     <meta property="og:description" content="${ogDescr}"> | 
					
						
							|  |  |  |     <meta name="twitter:card" content="summary_large_image"> | 
					
						
							| 
									
										
										
										
											2023-08-23 18:33:30 +02:00
										 |  |  |     <meta name="twitter:site" content="@mapcomplete.org"> | 
					
						
							| 
									
										
										
										
											2022-01-18 20:18:12 +01:00
										 |  |  |     <meta name="twitter:creator" content="@pietervdvn"> | 
					
						
							|  |  |  |     <meta name="twitter:title" content="${ogTitle}"> | 
					
						
							|  |  |  |     <meta name="twitter:description" content="${ogDescr}"> | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     <meta name="twitter:image" content="${twitterImage}">`
 | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     let icon = layout.icon | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |     if (icon.startsWith("<?xml") || icon.startsWith("<svg")) { | 
					
						
							|  |  |  |         // This already is an svg
 | 
					
						
							| 
									
										
										
										
											2023-02-09 16:19:08 +01:00
										 |  |  |         icon = `./public/assets/generated/images/${layout.id}_icon.svg` | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         writeFileSync(icon, layout.icon) | 
					
						
							| 
									
										
										
										
											2020-11-06 03:17:27 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-17 14:17:33 +01:00
										 |  |  |     const apple_icons = [] | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     for (const icon of whiteIcons) { | 
					
						
							|  |  |  |         if (!existsSync(icon)) { | 
					
						
							| 
									
										
										
										
											2022-02-06 12:51:23 +01:00
										 |  |  |             continue | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         const size = icon.replace(/[^0-9]/g, "") | 
					
						
							|  |  |  |         apple_icons.push(`<link rel="apple-touch-icon" sizes="${size}x${size}" href="${icon}">`) | 
					
						
							| 
									
										
										
										
											2021-03-17 14:17:33 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-09 00:05:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-16 20:03:19 +01:00
										 |  |  |     let themeSpecific = [ | 
					
						
							|  |  |  |         `<title>${ogTitle}</title>`, | 
					
						
							|  |  |  |         `<link rel="manifest" href="${enc(layout.id)}.webmanifest">`, | 
					
						
							|  |  |  |         og, | 
					
						
							|  |  |  |         customCss, | 
					
						
							|  |  |  |         `<link rel="icon" href="${icon}" sizes="any" type="image/svg+xml">`, | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ...apple_icons, | 
					
						
							| 
									
										
										
										
											2021-03-16 20:03:19 +01:00
										 |  |  |     ].join("\n") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-27 22:21:35 +02:00
										 |  |  |     const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title }) | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |     const templateLines = template.split("\n") | 
					
						
							|  |  |  |     const removeOtherLanguagesReference = templateLines.find( | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         (line) => line.indexOf("./src/UI/RemoveOtherLanguages.js") >= 0 | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2020-08-27 11:11:20 +02:00
										 |  |  |     let output = template | 
					
						
							| 
									
										
										
										
											2023-04-13 23:40:28 +02:00
										 |  |  |         .replace("Loading MapComplete, hang on...", asLangSpan(loadingText, "h1")) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         .replace( | 
					
						
							| 
									
										
										
										
											2023-05-25 13:19:42 +02:00
										 |  |  |             "Made with OpenStreetMap", | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |             Translations.t.general.poweredByOsm.textFor(targetLanguage) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-12-21 18:35:31 +01:00
										 |  |  |         .replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific) | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |         .replace( | 
					
						
							|  |  |  |             /<!-- CSP -->/, | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  |             await generateCsp(layout, layoutJson, { | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |                 scriptSrcs: [`'sha256-${removeOtherLanguagesHash}'`], | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |         .replace(removeOtherLanguagesReference, "<script>" + removeOtherLanguages + "</script>") | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         .replace( | 
					
						
							|  |  |  |             /<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s, | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |             asLangSpan(layout.shortDescription) | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |         ) | 
					
						
							|  |  |  |         .replace( | 
					
						
							|  |  |  |             /<!-- IMAGE-START -->.*<!-- IMAGE-END -->/s, | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |             "<img class='p-8 h-32 w-32 self-start' src='" + icon + "' />" | 
					
						
							| 
									
										
										
										
											2023-06-14 20:39:36 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-05-25 10:46:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         .replace( | 
					
						
							| 
									
										
										
										
											2023-09-28 03:00:22 +02:00
										 |  |  |             /.*\/src\/index\.ts.*/, | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |             `<script type="module" src="./index_${layout.id}.ts"></script>` | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-10-16 13:05:45 +02:00
										 |  |  |         .replace("Version", Constants.vNumber) | 
					
						
							| 
									
										
										
										
											2023-02-12 18:32:10 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     return output | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | async function createIndexFor(theme: LayoutConfig) { | 
					
						
							|  |  |  |     const filename = "index_" + theme.id + ".ts" | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const imports = [ | 
					
						
							|  |  |  |         `import layout from "./src/assets/generated/themes/${theme.id}.json"`, | 
					
						
							|  |  |  |         `import { ThemeMetaTagging } from "./src/assets/generated/metatagging/${theme.id}"`, | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2023-12-06 12:12:53 +01:00
										 |  |  |     for (const layerName of Constants.added_by_default) { | 
					
						
							|  |  |  |         imports.push(`import ${layerName} from "./src/assets/generated/layers/${layerName}.json"`) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-09-22 11:20:22 +02:00
										 |  |  |     writeFileSync(filename, imports.join("\n") + "\n") | 
					
						
							| 
									
										
										
										
											2023-07-16 03:26:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-06 12:12:53 +01:00
										 |  |  |     const addLayers = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (const layerName of Constants.added_by_default) { | 
					
						
							|  |  |  |         addLayers.push(`    layout.layers.push(<any> ${layerName})`) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     codeTemplate = codeTemplate.replace("    // LAYOUT.ADD_LAYERS", addLayers.join("\n")) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-21 18:35:31 +01:00
										 |  |  |     appendFileSync(filename, codeTemplate) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | function createDir(path) { | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |     if (!existsSync(path)) { | 
					
						
							|  |  |  |         mkdirSync(path) | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-11-17 16:29:51 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | async function main(): Promise<void> { | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |     const alreadyWritten = [] | 
					
						
							| 
									
										
										
										
											2023-02-09 16:19:08 +01:00
										 |  |  |     createDir("./public/assets/") | 
					
						
							|  |  |  |     createDir("./public/assets/generated") | 
					
						
							|  |  |  |     createDir("./public/assets/generated/images") | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const blacklist = [ | 
					
						
							|  |  |  |         "", | 
					
						
							|  |  |  |         "test", | 
					
						
							|  |  |  |         ".", | 
					
						
							|  |  |  |         "..", | 
					
						
							|  |  |  |         "manifest", | 
					
						
							|  |  |  |         "index", | 
					
						
							|  |  |  |         "land", | 
					
						
							|  |  |  |         "preferences", | 
					
						
							|  |  |  |         "account", | 
					
						
							|  |  |  |         "openstreetmap", | 
					
						
							|  |  |  |         "custom", | 
					
						
							|  |  |  |         "theme", | 
					
						
							|  |  |  |     ] | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |     // @ts-ignore
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const all: LayoutConfigJson[] = all_known_layouts.themes | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |     const args = process.argv | 
					
						
							|  |  |  |     const theme = args[2] | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |     if (theme !== undefined) { | 
					
						
							|  |  |  |         console.warn("Only generating layout " + theme) | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |     for (const i in all) { | 
					
						
							|  |  |  |         const layoutConfigJson: LayoutConfigJson = all[i] | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  |         if (theme !== undefined && layoutConfigJson.id !== theme) { | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |             continue | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-04-18 02:39:30 +02:00
										 |  |  |         const layout = new LayoutConfig(layoutConfigJson, true) | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |         const layoutName = layout.id | 
					
						
							|  |  |  |         if (blacklist.indexOf(layoutName.toLowerCase()) >= 0) { | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             console.log(`Skipping a layout with name${layoutName}, it is on the blacklist`) | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         const err = (err) => { | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |             if (err !== null) { | 
					
						
							|  |  |  |                 console.log("Could not write manifest for ", layoutName, " because ", err) | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         } | 
					
						
							|  |  |  |         const { manifest, whiteIcons } = await createManifest(layout, alreadyWritten) | 
					
						
							|  |  |  |         const manif = JSON.stringify(manifest, undefined, 2) | 
					
						
							|  |  |  |         const manifestLocation = encodeURIComponent(layout.id.toLowerCase()) + ".webmanifest" | 
					
						
							| 
									
										
										
										
											2023-02-06 01:47:11 +01:00
										 |  |  |         writeFile("public/" + manifestLocation, manif, err) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Create a landing page for the given theme
 | 
					
						
							| 
									
										
										
										
											2023-11-19 16:31:58 +01:00
										 |  |  |         const landing = await createLandingPage( | 
					
						
							|  |  |  |             layout, | 
					
						
							|  |  |  |             layoutConfigJson, | 
					
						
							|  |  |  |             manifest, | 
					
						
							|  |  |  |             whiteIcons, | 
					
						
							|  |  |  |             alreadyWritten | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2023-07-16 03:26:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-12 14:22:14 +02:00
										 |  |  |         writeFile(enc(layout.id) + ".html", landing, err) | 
					
						
							|  |  |  |         await createIndexFor(layout) | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const { manifest } = await createManifest( | 
					
						
							|  |  |  |         new LayoutConfig({ | 
					
						
							|  |  |  |             icon: "./assets/svg/mapcomplete_logo.svg", | 
					
						
							|  |  |  |             id: "index", | 
					
						
							|  |  |  |             layers: [], | 
					
						
							|  |  |  |             socialImage: "assets/SocialImage.png", | 
					
						
							|  |  |  |             startLat: 0, | 
					
						
							|  |  |  |             startLon: 0, | 
					
						
							|  |  |  |             startZoom: 0, | 
					
						
							|  |  |  |             title: { en: "MapComplete" }, | 
					
						
							|  |  |  |             description: { en: "A thematic map viewer and editor based on OpenStreetMap" }, | 
					
						
							|  |  |  |         }), | 
					
						
							| 
									
										
										
										
											2023-10-16 14:27:05 +02:00
										 |  |  |         alreadyWritten | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2022-03-08 04:09:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |     const manif = JSON.stringify(manifest, undefined, 2) | 
					
						
							| 
									
										
										
										
											2023-02-06 01:47:11 +01:00
										 |  |  |     writeFileSync("public/index.webmanifest", manif) | 
					
						
							| 
									
										
										
										
											2020-07-26 02:01:34 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-10-25 17:26:15 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-11 19:04:31 +02:00
										 |  |  | ScriptUtils.fixUtils() | 
					
						
							| 
									
										
										
										
											2022-02-06 01:57:33 +01:00
										 |  |  | main().then(() => { | 
					
						
							|  |  |  |     console.log("All done!") | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | }) |