From 0db16b9a7e8341e29ba36e6365c7248372311b94 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Mon, 5 Jul 2021 23:21:02 +0300 Subject: [PATCH] fix some merge issues --- main.js | 1141 +++++++++++++++++++++++------------------- modules/load.js | 672 +++++++++++++------------ modules/save.js | 462 ++++++++++------- modules/ui/layers.js | 1 + modules/ui/style.js | 1000 +++++++++++++++++++----------------- modules/ui/tools.js | 2 + 6 files changed, 1789 insertions(+), 1489 deletions(-) diff --git a/main.js b/main.js index 3d1aea76..9c6e1dfa 100644 --- a/main.js +++ b/main.js @@ -1,113 +1,113 @@ // Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2021. MIT License // https://github.com/Azgaar/Fantasy-Map-Generator -"use strict"; -const version = "1.63"; // generator version -document.title += " v" + version; +'use strict'; +const version = '1.63'; // generator version +document.title += ' v' + version; -// Switches to disable/enable logging features +// Logging constants const PRODUCTION = window.location.host; -const DEBUG = localStorage.getItem("debug"); +const DEBUG = localStorage.getItem('debug'); const INFO = DEBUG || !PRODUCTION; const TIME = DEBUG || !PRODUCTION; const WARN = true; const ERROR = true; // if map version is not stored, clear localStorage and show a message -if (rn(localStorage.getItem("version"), 1) !== rn(version, 1)) { +if (rn(localStorage.getItem('version'), 1) !== rn(version, 1)) { localStorage.clear(); setTimeout(showWelcomeMessage, 8000); } // append svg layers (in default order) -let svg = d3.select("#map"); -let defs = svg.select("#deftemp"); -let viewbox = svg.select("#viewbox"); -let scaleBar = svg.select("#scaleBar"); -let legend = svg.append("g").attr("id", "legend"); -let ocean = viewbox.append("g").attr("id", "ocean"); -let oceanLayers = ocean.append("g").attr("id", "oceanLayers"); -let oceanPattern = ocean.append("g").attr("id", "oceanPattern"); -let lakes = viewbox.append("g").attr("id", "lakes"); -let landmass = viewbox.append("g").attr("id", "landmass"); -let texture = viewbox.append("g").attr("id", "texture"); -let terrs = viewbox.append("g").attr("id", "terrs"); -let biomes = viewbox.append("g").attr("id", "biomes"); -let cells = viewbox.append("g").attr("id", "cells"); -let gridOverlay = viewbox.append("g").attr("id", "gridOverlay"); -let coordinates = viewbox.append("g").attr("id", "coordinates"); -let compass = viewbox.append("g").attr("id", "compass"); -let rivers = viewbox.append("g").attr("id", "rivers"); -let terrain = viewbox.append("g").attr("id", "terrain"); -let relig = viewbox.append("g").attr("id", "relig"); -let cults = viewbox.append("g").attr("id", "cults"); -let regions = viewbox.append("g").attr("id", "regions"); -let statesBody = regions.append("g").attr("id", "statesBody"); -let statesHalo = regions.append("g").attr("id", "statesHalo"); -let provs = viewbox.append("g").attr("id", "provs"); -let zones = viewbox.append("g").attr("id", "zones").style("display", "none"); -let borders = viewbox.append("g").attr("id", "borders"); -let stateBorders = borders.append("g").attr("id", "stateBorders"); -let provinceBorders = borders.append("g").attr("id", "provinceBorders"); -let routes = viewbox.append("g").attr("id", "routes"); -let roads = routes.append("g").attr("id", "roads"); -let trails = routes.append("g").attr("id", "trails"); -let searoutes = routes.append("g").attr("id", "searoutes"); -let temperature = viewbox.append("g").attr("id", "temperature"); -let coastline = viewbox.append("g").attr("id", "coastline"); -let ice = viewbox.append("g").attr("id", "ice").style("display", "none"); -let prec = viewbox.append("g").attr("id", "prec").style("display", "none"); -let population = viewbox.append("g").attr("id", "population"); -let goods = viewbox.append("g").attr("id", "goods"); -let emblems = viewbox.append("g").attr("id", "emblems").style("display", "none"); -let labels = viewbox.append("g").attr("id", "labels"); -let icons = viewbox.append("g").attr("id", "icons"); -let burgIcons = icons.append("g").attr("id", "burgIcons"); -let anchors = icons.append("g").attr("id", "anchors"); -let armies = viewbox.append("g").attr("id", "armies").style("display", "none"); -let markers = viewbox.append("g").attr("id", "markers").style("display", "none"); -let fogging = viewbox.append("g").attr("id", "fogging-cont").attr("mask", "url(#fog)").append("g").attr("id", "fogging").style("display", "none"); -let ruler = viewbox.append("g").attr("id", "ruler").style("display", "none"); -let debug = viewbox.append("g").attr("id", "debug"); +let svg = d3.select('#map'); +let defs = svg.select('#deftemp'); +let viewbox = svg.select('#viewbox'); +let scaleBar = svg.select('#scaleBar'); +let legend = svg.append('g').attr('id', 'legend'); +let ocean = viewbox.append('g').attr('id', 'ocean'); +let oceanLayers = ocean.append('g').attr('id', 'oceanLayers'); +let oceanPattern = ocean.append('g').attr('id', 'oceanPattern'); +let lakes = viewbox.append('g').attr('id', 'lakes'); +let landmass = viewbox.append('g').attr('id', 'landmass'); +let texture = viewbox.append('g').attr('id', 'texture'); +let terrs = viewbox.append('g').attr('id', 'terrs'); +let biomes = viewbox.append('g').attr('id', 'biomes'); +let cells = viewbox.append('g').attr('id', 'cells'); +let gridOverlay = viewbox.append('g').attr('id', 'gridOverlay'); +let coordinates = viewbox.append('g').attr('id', 'coordinates'); +let compass = viewbox.append('g').attr('id', 'compass'); +let rivers = viewbox.append('g').attr('id', 'rivers'); +let terrain = viewbox.append('g').attr('id', 'terrain'); +let relig = viewbox.append('g').attr('id', 'relig'); +let cults = viewbox.append('g').attr('id', 'cults'); +let regions = viewbox.append('g').attr('id', 'regions'); +let statesBody = regions.append('g').attr('id', 'statesBody'); +let statesHalo = regions.append('g').attr('id', 'statesHalo'); +let provs = viewbox.append('g').attr('id', 'provs'); +let zones = viewbox.append('g').attr('id', 'zones').style('display', 'none'); +let borders = viewbox.append('g').attr('id', 'borders'); +let stateBorders = borders.append('g').attr('id', 'stateBorders'); +let provinceBorders = borders.append('g').attr('id', 'provinceBorders'); +let routes = viewbox.append('g').attr('id', 'routes'); +let roads = routes.append('g').attr('id', 'roads'); +let trails = routes.append('g').attr('id', 'trails'); +let searoutes = routes.append('g').attr('id', 'searoutes'); +let temperature = viewbox.append('g').attr('id', 'temperature'); +let coastline = viewbox.append('g').attr('id', 'coastline'); +let ice = viewbox.append('g').attr('id', 'ice').style('display', 'none'); +let prec = viewbox.append('g').attr('id', 'prec').style('display', 'none'); +let population = viewbox.append('g').attr('id', 'population'); +let goods = viewbox.append('g').attr('id', 'goods'); +let emblems = viewbox.append('g').attr('id', 'emblems').style('display', 'none'); +let labels = viewbox.append('g').attr('id', 'labels'); +let icons = viewbox.append('g').attr('id', 'icons'); +let burgIcons = icons.append('g').attr('id', 'burgIcons'); +let anchors = icons.append('g').attr('id', 'anchors'); +let armies = viewbox.append('g').attr('id', 'armies').style('display', 'none'); +let markers = viewbox.append('g').attr('id', 'markers').style('display', 'none'); +let fogging = viewbox.append('g').attr('id', 'fogging-cont').attr('mask', 'url(#fog)').append('g').attr('id', 'fogging').style('display', 'none'); +let ruler = viewbox.append('g').attr('id', 'ruler').style('display', 'none'); +let debug = viewbox.append('g').attr('id', 'debug'); // lake and coast groups -lakes.append("g").attr("id", "freshwater"); -lakes.append("g").attr("id", "salt"); -lakes.append("g").attr("id", "sinkhole"); -lakes.append("g").attr("id", "frozen"); -lakes.append("g").attr("id", "lava"); -lakes.append("g").attr("id", "dry"); -coastline.append("g").attr("id", "sea_island"); -coastline.append("g").attr("id", "lake_island"); +lakes.append('g').attr('id', 'freshwater'); +lakes.append('g').attr('id', 'salt'); +lakes.append('g').attr('id', 'sinkhole'); +lakes.append('g').attr('id', 'frozen'); +lakes.append('g').attr('id', 'lava'); +lakes.append('g').attr('id', 'dry'); +coastline.append('g').attr('id', 'sea_island'); +coastline.append('g').attr('id', 'lake_island'); -labels.append("g").attr("id", "states"); -labels.append("g").attr("id", "addedLabels"); +labels.append('g').attr('id', 'states'); +labels.append('g').attr('id', 'addedLabels'); -let burgLabels = labels.append("g").attr("id", "burgLabels"); -burgIcons.append("g").attr("id", "cities"); -burgLabels.append("g").attr("id", "cities"); -anchors.append("g").attr("id", "cities"); +let burgLabels = labels.append('g').attr('id', 'burgLabels'); +burgIcons.append('g').attr('id', 'cities'); +burgLabels.append('g').attr('id', 'cities'); +anchors.append('g').attr('id', 'cities'); -burgIcons.append("g").attr("id", "towns"); -burgLabels.append("g").attr("id", "towns"); -anchors.append("g").attr("id", "towns"); +burgIcons.append('g').attr('id', 'towns'); +burgLabels.append('g').attr('id', 'towns'); +anchors.append('g').attr('id', 'towns'); // population groups -population.append("g").attr("id", "rural"); -population.append("g").attr("id", "urban"); +population.append('g').attr('id', 'rural'); +population.append('g').attr('id', 'urban'); // emblem groups -emblems.append("g").attr("id", "burgEmblems"); -emblems.append("g").attr("id", "provinceEmblems"); -emblems.append("g").attr("id", "stateEmblems"); +emblems.append('g').attr('id', 'burgEmblems'); +emblems.append('g').attr('id', 'provinceEmblems'); +emblems.append('g').attr('id', 'stateEmblems'); // fogging -fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%"); -fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("fill", "#e8f0f6").attr("filter", "url(#splotch)"); +fogging.append('rect').attr('x', 0).attr('y', 0).attr('width', '100%').attr('height', '100%'); +fogging.append('rect').attr('x', 0).attr('y', 0).attr('width', '100%').attr('height', '100%').attr('fill', '#e8f0f6').attr('filter', 'url(#splotch)'); // assign events separately as not a viewbox child -scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => editUnits()); -legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend()); +scaleBar.on('mousemove', () => tip('Click to open Units Editor')).on('click', () => editUnits()); +legend.on('mousemove', () => tip('Drag to change the position. Click to hide the legend')).on('click', () => clearLegend()); // main data variables let grid = {}; // initial grapg based on jittered square grid and data @@ -123,7 +123,7 @@ let customization = 0; // 0 - no; 1 = heightmap draw; 2 - states draw; 3 - add s let biomesData = applyDefaultBiomesSystem(); let nameBases = Names.getNameBases(); // cultures-related data -const fonts = ["Almendra+SC", "Georgia", "Arial", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New"]; // default web-safe fonts +const fonts = ['Almendra+SC', 'Georgia', 'Arial', 'Times+New+Roman', 'Comic+Sans+MS', 'Lucida+Sans+Unicode', 'Courier+New']; // default web-safe fonts let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation @@ -132,30 +132,30 @@ const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with defaul let scale = 1, viewX = 0, viewY = 0; -const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomed); +const zoom = d3.zoom().scaleExtent([1, 20]).on('zoom', zoomed); // default options let options = {pinNotes: false}; // options object let mapCoordinates = {}; // map coordinates on globe options.winds = [225, 45, 225, 315, 135, 315]; // default wind directions -let populationRate = +document.getElementById("populationRateInput").value; -let urbanization = +document.getElementById("urbanizationInput").value; +let populationRate = +document.getElementById('populationRateInput').value; +let urbanization = +document.getElementById('urbanizationInput').value; applyStoredOptions(); let graphWidth = +mapWidthInput.value, graphHeight = +mapHeightInput.value; // voronoi graph extention, cannot be changed arter generation let svgWidth = graphWidth, svgHeight = graphHeight; // svg canvas resolution, can be changed -landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); -oceanPattern.append("rect").attr("fill", "url(#oceanic)").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); -oceanLayers.append("rect").attr("id", "oceanBase").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight); +landmass.append('rect').attr('x', 0).attr('y', 0).attr('width', graphWidth).attr('height', graphHeight); +oceanPattern.append('rect').attr('fill', 'url(#oceanic)').attr('x', 0).attr('y', 0).attr('width', graphWidth).attr('height', graphHeight); +oceanLayers.append('rect').attr('id', 'oceanBase').attr('x', 0).attr('y', 0).attr('width', graphWidth).attr('height', graphHeight); void (function removeLoading() { - d3.select("#loading").transition().duration(4000).style("opacity", 0).remove(); - d3.select("#initial").transition().duration(4000).attr("opacity", 0).remove(); - d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1); - d3.select("#tooltip").transition().duration(4000).style("opacity", 1); + d3.select('#loading').transition().duration(4000).style('opacity', 0).remove(); + d3.select('#initial').transition().duration(4000).attr('opacity', 0).remove(); + d3.select('#optionsContainer').transition().duration(3000).style('opacity', 1); + d3.select('#tooltip').transition().duration(4000).style('opacity', 1); })(); // decide which map should be loaded or generated on page load @@ -164,58 +164,57 @@ void (function checkLoadParameters() { const params = url.searchParams; // of there is a valid maplink, try to load .map file from URL - if (params.get("maplink")) { - WARN && console.warn("Load map from URL"); - const maplink = params.get("maplink"); + if (params.get('maplink')) { + WARN && console.warn('Load map from URL'); + const maplink = params.get('maplink'); const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; const valid = pattern.test(maplink); - if (valid) { - loadMapFromURL(maplink, 1); - return; - } else showUploadErrorMessage("Map link is not a valid URL", maplink); + if (valid) return loadMapFromURL(maplink, 1); + + showUploadErrorMessage('Map link is not a valid URL', maplink); } // if there is a seed (user of MFCG provided), generate map for it - if (params.get("seed")) { - WARN && console.warn("Generate map for seed"); + if (params.get('seed')) { + WARN && console.warn('Generate map for seed'); generateMapOnLoad(); return; } // open latest map if option is active and map is stored - if (onloadMap.value === "saved") { - ldb.get("lastMap", blob => { + if (onloadMap.value === 'saved') { + ldb.get('lastMap', (blob) => { if (blob) { - WARN && console.warn("Load last saved map"); + WARN && console.warn('Load last saved map'); try { uploadMap(blob); } catch (error) { ERROR && console.error(error); - WARN && console.warn("Cannot load stored map, random map to be generated"); + WARN && console.warn('Cannot load stored map, random map to be generated'); generateMapOnLoad(); } } else { - ERROR && console.error("No map stored, random map to be generated"); + ERROR && console.error('No map stored, random map to be generated'); generateMapOnLoad(); } }); return; } - WARN && console.warn("Generate random map"); + WARN && console.warn('Generate random map'); generateMapOnLoad(); })(); function loadMapFromURL(maplink, random) { const URL = decodeURIComponent(maplink); - fetch(URL, {method: "GET", mode: "cors"}) - .then(response => { + fetch(URL, {method: 'GET', mode: 'cors'}) + .then((response) => { if (response.ok) return response.blob(); - throw new Error("Cannot load map from URL"); + throw new Error('Cannot load map from URL'); }) - .then(blob => uploadMap(blob)) - .catch(error => { + .then((blob) => uploadMap(blob)) + .catch((error) => { showUploadErrorMessage(error.message, URL, random); if (random) generateMapOnLoad(); }); @@ -223,15 +222,15 @@ function loadMapFromURL(maplink, random) { function showUploadErrorMessage(error, URL, random) { ERROR && console.error(error); - alertMessage.innerHTML = `Cannot load map from the ${link(URL, "link provided")}. - ${random ? `A new random map is generated. ` : ""} + alertMessage.innerHTML = `Cannot load map from the ${link(URL, 'link provided')}. + ${random ? `A new random map is generated. ` : ''} Please ensure the linked file is reachable and CORS is allowed on server side`; - $("#alert").dialog({ - title: "Loading error", - width: "32em", + $('#alert').dialog({ + title: 'Loading error', + width: '32em', buttons: { OK: function () { - $(this).dialog("close"); + $(this).dialog('close'); } } }); @@ -249,10 +248,10 @@ function focusOn() { const url = new URL(window.location.href); const params = url.searchParams; - if (params.get("from") === "MFCG" && document.referrer) { - if (params.get("seed").length === 13) { + if (params.get('from') === 'MFCG' && document.referrer) { + if (params.get('seed').length === 13) { // show back burg from MFCG - params.set("burg", params.get("seed").slice(-4)); + params.set('burg', params.get('seed').slice(-4)); } else { // select burg for MFCG findBurgForMFCG(params); @@ -260,17 +259,17 @@ function focusOn() { } } - const s = +params.get("scale") || 8; - let x = +params.get("x"); - let y = +params.get("y"); + const s = +params.get('scale') || 8; + let x = +params.get('x'); + let y = +params.get('y'); - const c = +params.get("cell"); + const c = +params.get('cell'); if (c) { x = pack.cells.p[c][0]; y = pack.cells.p[c][1]; } - const b = +params.get("burg"); + const b = +params.get('burg'); if (b && pack.burgs[b]) { x = pack.burgs[b].x; y = pack.burgs[b].y; @@ -281,18 +280,14 @@ function focusOn() { // find burg for MFCG and focus on it function findBurgForMFCG(params) { - const cells = pack.cells, - burgs = pack.burgs; - if (pack.burgs.length < 2) { - ERROR && console.error("Cannot select a burg for MFCG"); - return; - } + const {cells, burgs} = pack; + if (burgs.length < 2) return ERROR && console.error('Cannot select a burg for MFCG'); // used for selection - const size = +params.get("size"); - const coast = +params.get("coast"); - const port = +params.get("port"); - const river = +params.get("river"); + const size = +params.get('size'); + const coast = +params.get('coast'); + const port = +params.get('port'); + const river = +params.get('river'); let selection = defineSelection(coast, port, river); if (!selection.length) selection = defineSelection(coast, !port, !river); @@ -300,11 +295,11 @@ function findBurgForMFCG(params) { if (!selection.length) selection = [burgs[1]]; // select first if nothing is found function defineSelection(coast, port, river) { - if (port && river) return burgs.filter(b => b.port && cells.r[b.cell]); - if (!port && coast && river) return burgs.filter(b => !b.port && cells.t[b.cell] === 1 && cells.r[b.cell]); - if (!coast && !river) return burgs.filter(b => cells.t[b.cell] !== 1 && !cells.r[b.cell]); - if (!coast && river) return burgs.filter(b => cells.t[b.cell] !== 1 && cells.r[b.cell]); - if (coast && river) return burgs.filter(b => cells.t[b.cell] === 1 && cells.r[b.cell]); + if (port && river) return burgs.filter((b) => b.port && cells.r[b.cell]); + if (!port && coast && river) return burgs.filter((b) => !b.port && cells.t[b.cell] === 1 && cells.r[b.cell]); + if (!coast && !river) return burgs.filter((b) => cells.t[b.cell] !== 1 && !cells.r[b.cell]); + if (!coast && river) return burgs.filter((b) => cells.t[b.cell] !== 1 && cells.r[b.cell]); + if (coast && river) return burgs.filter((b) => cells.t[b.cell] === 1 && cells.r[b.cell]); return []; } @@ -312,45 +307,73 @@ function findBurgForMFCG(params) { const selected = d3.scan(selection, (a, b) => Math.abs(a.population - size) - Math.abs(b.population - size)); const burgId = selection[selected].i; if (!burgId) { - ERROR && console.error("Cannot select a burg for MFCG"); + ERROR && console.error('Cannot select a burg for MFCG'); return; } const b = burgs[burgId]; const referrer = new URL(document.referrer); for (let p of referrer.searchParams) { - if (p[0] === "name") b.name = p[1]; - else if (p[0] === "size") b.population = +p[1]; - else if (p[0] === "seed") b.MFCG = +p[1]; - else if (p[0] === "shantytown") b.shanty = +p[1]; + if (p[0] === 'name') b.name = p[1]; + else if (p[0] === 'size') b.population = +p[1]; + else if (p[0] === 'seed') b.MFCG = +p[1]; + else if (p[0] === 'shantytown') b.shanty = +p[1]; else b[p[0]] = +p[1]; // other parameters } b.MFCGlink = document.referrer; // set direct link to MFCG - if (params.get("name") && params.get("name") != "null") b.name = params.get("name"); + if (params.get('name') && params.get('name') != 'null') b.name = params.get('name'); const label = burgLabels.select("[data-id='" + burgId + "']"); if (label.size()) { label .text(b.name) - .classed("drag", true) - .on("mouseover", function () { - d3.select(this).classed("drag", false); - label.on("mouseover", null); + .classed('drag', true) + .on('mouseover', function () { + d3.select(this).classed('drag', false); + label.on('mouseover', null); }); } zoomTo(b.x, b.y, 8, 1600); invokeActiveZooming(); - tip("Here stands the glorious city of " + b.name, true, "success", 15000); + tip('Here stands the glorious city of ' + b.name, true, 'success', 15000); } // apply default biomes data function applyDefaultBiomesSystem() { - const name = ["Marine", "Hot desert", "Cold desert", "Savanna", "Grassland", "Tropical seasonal forest", "Temperate deciduous forest", "Tropical rainforest", "Temperate rainforest", "Taiga", "Tundra", "Glacier", "Wetland"]; - const color = ["#466eab", "#fbe79f", "#b5b887", "#d2d082", "#c8d68f", "#b6d95d", "#29bc56", "#7dcb35", "#409c43", "#4b6b32", "#96784b", "#d5e7eb", "#0b9131"]; + const name = [ + 'Marine', + 'Hot desert', + 'Cold desert', + 'Savanna', + 'Grassland', + 'Tropical seasonal forest', + 'Temperate deciduous forest', + 'Tropical rainforest', + 'Temperate rainforest', + 'Taiga', + 'Tundra', + 'Glacier', + 'Wetland' + ]; + const color = ['#466eab', '#fbe79f', '#b5b887', '#d2d082', '#c8d68f', '#b6d95d', '#29bc56', '#7dcb35', '#409c43', '#4b6b32', '#96784b', '#d5e7eb', '#0b9131']; const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12]; const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150]; - const icons = [{}, {dune: 3, cactus: 6, deadTree: 1}, {dune: 9, deadTree: 1}, {acacia: 1, grass: 9}, {grass: 1}, {acacia: 8, palm: 1}, {deciduous: 1}, {acacia: 5, palm: 3, deciduous: 1, swamp: 1}, {deciduous: 6, swamp: 1}, {conifer: 1}, {grass: 1}, {}, {swamp: 1}]; + const icons = [ + {}, + {dune: 3, cactus: 6, deadTree: 1}, + {dune: 9, deadTree: 1}, + {acacia: 1, grass: 9}, + {grass: 1}, + {acacia: 8, palm: 1}, + {deciduous: 1}, + {acacia: 5, palm: 3, deciduous: 1, swamp: 1}, + {deciduous: 6, swamp: 1}, + {conifer: 1}, + {grass: 1}, + {}, + {swamp: 1} + ]; const cost = [10, 200, 150, 60, 50, 70, 70, 80, 90, 200, 1000, 5000, 150]; // biome movement cost const biomesMartix = [ // hot ↔ cold [>19°C; <-4°C]; dry ↕ wet @@ -376,11 +399,11 @@ function applyDefaultBiomesSystem() { } function showWelcomeMessage() { - const post = link("https://www.patreon.com/posts/48228540", "Main changes:"); - const changelog = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "previous version"); - const reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit community"); - const discord = link("https://discordapp.com/invite/X7E84HU", "Discord server"); - const patreon = link("https://www.patreon.com/azgaar", "Patreon"); + const post = link('https://www.patreon.com/posts/48228540', 'Main changes:'); + const changelog = link('https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog', 'previous version'); + const reddit = link('https://www.reddit.com/r/FantasyMapGenerator', 'Reddit community'); + const discord = link('https://discordapp.com/invite/X7E84HU', 'Discord server'); + const patreon = link('https://www.patreon.com/azgaar', 'Patreon'); alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version ${version}. This version is compatible with ${changelog}, loaded .map files will be auto-updated. @@ -401,17 +424,17 @@ function showWelcomeMessage() {

Join our ${discord} and ${reddit} to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.

Thanks for all supporters on ${patreon}!`; - $("#alert").dialog({ + $('#alert').dialog({ resizable: false, - title: "Fantasy Map Generator update", - width: "28em", + title: 'Fantasy Map Generator update', + width: '28em', buttons: { OK: function () { - $(this).dialog("close"); + $(this).dialog('close'); } }, - position: {my: "center center-4em", at: "center", of: "svg"}, - close: () => localStorage.setItem("version", version) + position: {my: 'center center-4em', at: 'center', of: 'svg'}, + close: () => localStorage.setItem('version', version) }); } @@ -424,7 +447,7 @@ function zoomed() { scale = transform.k; viewX = transform.x; viewY = transform.y; - viewbox.attr("transform", transform); + viewbox.attr('transform', transform); // update grid only if view position if (positionDiff) drawCoordinates(); @@ -436,10 +459,10 @@ function zoomed() { } // zoom image converter overlay - const canvas = document.getElementById("canvas"); + const canvas = document.getElementById('canvas'); if (canvas && +canvas.style.opacity) { - const img = document.getElementById("image"); - const ctx = canvas.getContext("2d"); + const img = document.getElementById('image'); + const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.setTransform(scale, 0, 0, scale, viewX, viewY); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); @@ -469,110 +492,110 @@ function getViewBoxExtent() { // active zooming feature function invokeActiveZooming() { - if (coastline.select("#sea_island").size() && +coastline.select("#sea_island").attr("auto-filter")) { + if (coastline.select('#sea_island').size() && +coastline.select('#sea_island').attr('auto-filter')) { // toggle shade/blur filter for coatline on zoom - const filter = scale > 1.5 && scale <= 2.6 ? null : scale > 2.6 ? "url(#blurFilter)" : "url(#dropShadow)"; - coastline.select("#sea_island").attr("filter", filter); + const filter = scale > 1.5 && scale <= 2.6 ? null : scale > 2.6 ? 'url(#blurFilter)' : 'url(#dropShadow)'; + coastline.select('#sea_island').attr('filter', filter); } // rescale lables on zoom - if (labels.style("display") !== "none") { - labels.selectAll("g").each(function (d) { - if (this.id === "burgLabels") return; + if (labels.style('display') !== 'none') { + labels.selectAll('g').each(function (d) { + if (this.id === 'burgLabels') return; const desired = +this.dataset.size; const relative = Math.max(rn((desired + desired / scale) / 2, 2), 1); - this.getAttribute("font-size", relative); + this.getAttribute('font-size', relative); const hidden = hideLabels.checked && (relative * scale < 6 || relative * scale > 50); - if (hidden) this.classList.add("hidden"); - else this.classList.remove("hidden"); + if (hidden) this.classList.add('hidden'); + else this.classList.remove('hidden'); }); } // rescale emblems on zoom - if (emblems.style("display") !== "none") { - emblems.selectAll("g").each(function () { - const size = this.getAttribute("font-size") * scale; + if (emblems.style('display') !== 'none') { + emblems.selectAll('g').each(function () { + const size = this.getAttribute('font-size') * scale; const hidden = hideEmblems.checked && (size < 25 || size > 300); - if (hidden) this.classList.add("hidden"); - else this.classList.remove("hidden"); - if (!hidden && window.COArenderer && this.children.length && !this.children[0].getAttribute("href")) renderGroupCOAs(this); + if (hidden) this.classList.add('hidden'); + else this.classList.remove('hidden'); + if (!hidden && window.COArenderer && this.children.length && !this.children[0].getAttribute('href')) renderGroupCOAs(this); }); } // turn off ocean pattern if scale is big (improves performance) oceanPattern - .select("rect") - .attr("fill", scale > 10 ? "#fff" : "url(#oceanic)") - .attr("opacity", scale > 10 ? 0.2 : null); + .select('rect') + .attr('fill', scale > 10 ? '#fff' : 'url(#oceanic)') + .attr('opacity', scale > 10 ? 0.2 : null); // change states halo width if (!customization) { - const haloSize = rn(statesHalo.attr("data-width") / scale, 1); - statesHalo.attr("stroke-width", haloSize).style("display", haloSize > 3 ? "block" : "none"); + const haloSize = rn(statesHalo.attr('data-width') / scale, 1); + statesHalo.attr('stroke-width', haloSize).style('display', haloSize > 3 ? 'block' : 'none'); } // rescale map markers - if (+markers.attr("rescale") && markers.style("display") !== "none") { - markers.selectAll("use").each(function (d) { + if (+markers.attr('rescale') && markers.style('display') !== 'none') { + markers.selectAll('use').each(function (d) { const x = +this.dataset.x, y = +this.dataset.y, desired = +this.dataset.size; const size = Math.max(desired * 5 + 25 / scale, 1); d3.select(this) - .attr("x", x - size / 2) - .attr("y", y - size) - .attr("width", size) - .attr("height", size); + .attr('x', x - size / 2) + .attr('y', y - size) + .attr('width', size) + .attr('height', size); }); } // rescale rulers to have always the same size - if (ruler.style("display") !== "none") { + if (ruler.style('display') !== 'none') { const size = rn((10 / scale ** 0.3) * 2, 2); - ruler.selectAll("text").attr("font-size", size); + ruler.selectAll('text').attr('font-size', size); } } async function renderGroupCOAs(g) { - const [group, type] = g.id === "burgEmblems" ? [pack.burgs, "burg"] : g.id === "provinceEmblems" ? [pack.provinces, "province"] : [pack.states, "state"]; + const [group, type] = g.id === 'burgEmblems' ? [pack.burgs, 'burg'] : g.id === 'provinceEmblems' ? [pack.provinces, 'province'] : [pack.states, 'state']; for (let use of g.children) { const i = +use.dataset.i; - const id = type + "COA" + i; + const id = type + 'COA' + i; COArenderer.trigger(id, group[i].coa); - use.setAttribute("href", "#" + id); + use.setAttribute('href', '#' + id); } } // add drag to upload logic, pull request from @evyatron void (function addDragToUpload() { - document.addEventListener("dragover", function (e) { + document.addEventListener('dragover', function (e) { e.stopPropagation(); e.preventDefault(); - document.getElementById("mapOverlay").style.display = null; + document.getElementById('mapOverlay').style.display = null; }); - document.addEventListener("dragleave", function (e) { - document.getElementById("mapOverlay").style.display = "none"; + document.addEventListener('dragleave', function (e) { + document.getElementById('mapOverlay').style.display = 'none'; }); - document.addEventListener("drop", function (e) { + document.addEventListener('drop', function (e) { e.stopPropagation(); e.preventDefault(); - const overlay = document.getElementById("mapOverlay"); - overlay.style.display = "none"; + const overlay = document.getElementById('mapOverlay'); + overlay.style.display = 'none'; if (e.dataTransfer.items == null || e.dataTransfer.items.length !== 1) return; // no files or more than one const file = e.dataTransfer.items[0].getAsFile(); - if (file.name.indexOf(".map") == -1) { + if (file.name.indexOf('.map') == -1) { // not a .map file - alertMessage.innerHTML = "Please upload a .map file you have previously downloaded"; - $("#alert").dialog({ + alertMessage.innerHTML = 'Please upload a .map file you have previously downloaded'; + $('#alert').dialog({ resizable: false, - title: "Invalid file format", - position: {my: "center", at: "center", of: "svg"}, + title: 'Invalid file format', + position: {my: 'center', at: 'center', of: 'svg'}, buttons: { Close: function () { - $(this).dialog("close"); + $(this).dialog('close'); } } }); @@ -581,11 +604,11 @@ void (function addDragToUpload() { // all good - show uploading text and load the map overlay.style.display = null; - overlay.innerHTML = "Uploading..."; + overlay.innerHTML = 'Uploading...'; if (closeDialogs) closeDialogs(); uploadMap(file, () => { - overlay.style.display = "none"; - overlay.innerHTML = "Drop a .map file to open"; + overlay.style.display = 'none'; + overlay.innerHTML = 'Drop a .map file to open'; }); }); })(); @@ -595,7 +618,7 @@ function generate() { const timeStart = performance.now(); invokeActiveZooming(); generateSeed(); - INFO && console.group("Generated Map " + seed); + INFO && console.group('Generated Map ' + seed); applyMapSize(); randomizeOptions(); placePoints(); @@ -644,7 +667,7 @@ function generate() { WARN && console.warn(`TOTAL: ${rn((performance.now() - timeStart) / 1000, 2)}s`); showStatistics(); - INFO && console.groupEnd("Generated Map " + seed); + INFO && console.groupEnd('Generated Map ' + seed); } catch (error) { ERROR && console.error(error); clearMainTip(); @@ -652,24 +675,24 @@ function generate() { alertMessage.innerHTML = `An error is occured on map generation. Please retry.
If error is critical, clear the stored data and try again.

${parseError(error)}

`; - $("#alert").dialog({ + $('#alert').dialog({ resizable: false, - title: "Generation error", - width: "32em", + title: 'Generation error', + width: '32em', buttons: { - "Clear data": function () { + 'Clear data': function () { localStorage.clear(); - localStorage.setItem("version", version); + localStorage.setItem('version', version); }, Regenerate: function () { regenerateMap(); - $(this).dialog("close"); + $(this).dialog('close'); }, Ignore: function () { - $(this).dialog("close"); + $(this).dialog('close'); } }, - position: {my: "center", at: "center", of: "svg"} + position: {my: 'center', at: 'center', of: 'svg'} }); } } @@ -679,8 +702,8 @@ function generateSeed() { const first = !mapHistory[0]; const url = new URL(window.location.href); const params = url.searchParams; - const urlSeed = url.searchParams.get("seed"); - if (first && params.get("from") === "MFCG" && urlSeed.length === 13) seed = urlSeed.slice(0, -4); + const urlSeed = url.searchParams.get('seed'); + if (first && params.get('from') === 'MFCG' && urlSeed.length === 13) seed = urlSeed.slice(0, -4); else if (first && urlSeed) seed = urlSeed; else if (optionsSeed.value && optionsSeed.value != seed) seed = optionsSeed.value; else seed = Math.floor(Math.random() * 1e9).toString(); @@ -690,7 +713,7 @@ function generateSeed() { // Place points to calculate Voronoi diagram function placePoints() { - TIME && console.time("placePoints"); + TIME && console.time('placePoints'); const cellsDesired = +pointsInput.dataset.cells; const spacing = (grid.spacing = rn(Math.sqrt((graphWidth * graphHeight) / cellsDesired), 2)); // spacing between points before jirrering @@ -698,28 +721,28 @@ function placePoints() { grid.points = getJitteredGrid(graphWidth, graphHeight, spacing); // jittered square grid grid.cellsX = Math.floor((graphWidth + 0.5 * spacing) / spacing); grid.cellsY = Math.floor((graphHeight + 0.5 * spacing) / spacing); - TIME && console.timeEnd("placePoints"); + TIME && console.timeEnd('placePoints'); } // calculate Delaunay and then Voronoi diagram function calculateVoronoi(graph, points) { - TIME && console.time("calculateDelaunay"); + TIME && console.time('calculateDelaunay'); const n = points.length; const allPoints = points.concat(grid.boundary); const delaunay = Delaunator.from(allPoints); - TIME && console.timeEnd("calculateDelaunay"); + TIME && console.timeEnd('calculateDelaunay'); - TIME && console.time("calculateVoronoi"); + TIME && console.time('calculateVoronoi'); const voronoi = new Voronoi(delaunay, allPoints, n); graph.cells = voronoi.cells; graph.cells.i = n < 65535 ? Uint16Array.from(d3.range(n)) : Uint32Array.from(d3.range(n)); // array of indexes graph.vertices = voronoi.vertices; - TIME && console.timeEnd("calculateVoronoi"); + TIME && console.timeEnd('calculateVoronoi'); } // Mark features (ocean, lakes, islands) and calculate distance field function markFeatures() { - TIME && console.time("markFeatures"); + TIME && console.time('markFeatures'); Math.random = aleaPRNG(seed); // get the same result on heightmap edit in Erase mode const cells = grid.cells, @@ -737,7 +760,7 @@ function markFeatures() { const q = queue.pop(); if (cells.b[q]) border = true; - cells.c[q].forEach(c => { + cells.c[q].forEach((c) => { const cLand = heights[c] >= 20; if (land === cLand && !cells.f[c]) { cells.f[c] = i; @@ -748,19 +771,19 @@ function markFeatures() { } }); } - const type = land ? "island" : border ? "ocean" : "lake"; + const type = land ? 'island' : border ? 'ocean' : 'lake'; grid.features.push({i, land, border, type}); - queue[0] = cells.f.findIndex(f => !f); // find unmarked cell + queue[0] = cells.f.findIndex((f) => !f); // find unmarked cell } - TIME && console.timeEnd("markFeatures"); + TIME && console.timeEnd('markFeatures'); } function markupGridOcean() { - TIME && console.time("markupGridOcean"); + TIME && console.time('markupGridOcean'); markup(grid.cells, -2, -1, -10); - TIME && console.timeEnd("markupGridOcean"); + TIME && console.timeEnd('markupGridOcean'); } function markup(cells, start, increment, limit) { @@ -780,16 +803,16 @@ function markup(cells, start, increment, limit) { } function addLakesInDeepDepressions() { - TIME && console.time("addLakesInDeepDepressions"); + TIME && console.time('addLakesInDeepDepressions'); const {cells, features} = grid; const {c, h, b} = cells; - const ELEVATION_LIMIT = +document.getElementById("lakeElevationLimitOutput").value; + const ELEVATION_LIMIT = +document.getElementById('lakeElevationLimitOutput').value; if (ELEVATION_LIMIT === 80) return; for (const i of cells.i) { if (b[i] || h[i] < 20) continue; - const minHeight = d3.min(c[i].map(c => h[c])); + const minHeight = d3.min(c[i].map((c) => h[c])); if (h[i] > minHeight) continue; let deep = true; @@ -817,7 +840,7 @@ function addLakesInDeepDepressions() { // if not, add a lake if (deep) { - const lakeCells = [i].concat(c[i].filter(n => h[n] === h[i])); + const lakeCells = [i].concat(c[i].filter((n) => h[n] === h[i])); addLake(lakeCells); } } @@ -825,39 +848,39 @@ function addLakesInDeepDepressions() { function addLake(lakeCells) { const f = features.length; - lakeCells.forEach(i => { + lakeCells.forEach((i) => { cells.h[i] = 19; cells.t[i] = -1; cells.f[i] = f; - c[i].forEach(n => !lakeCells.includes(n) && (cells.t[c] = 1)); + c[i].forEach((n) => !lakeCells.includes(n) && (cells.t[c] = 1)); }); - features.push({i: f, land: false, border: false, type: "lake"}); + features.push({i: f, land: false, border: false, type: 'lake'}); } - TIME && console.timeEnd("addLakesInDeepDepressions"); + TIME && console.timeEnd('addLakesInDeepDepressions'); } // near sea lakes usually get a lot of water inflow, most of them should brake threshold and flow out to sea (see Ancylus Lake) function openNearSeaLakes() { - if (templateInput.value === "Atoll") return; // no need for Atolls + if (templateInput.value === 'Atoll') return; // no need for Atolls const cells = grid.cells; const features = grid.features; - if (!features.find(f => f.type === "lake")) return; // no lakes - TIME && console.time("openLakes"); + if (!features.find((f) => f.type === 'lake')) return; // no lakes + TIME && console.time('openLakes'); const LIMIT = 22; // max height that can be breached by water for (const i of cells.i) { const lake = cells.f[i]; - if (features[lake].type !== "lake") continue; // not a lake cell + if (features[lake].type !== 'lake') continue; // not a lake cell check_neighbours: for (const c of cells.c[i]) { if (cells.t[c] !== 1 || cells.h[c] > LIMIT) continue; // water cannot brake this for (const n of cells.c[c]) { const ocean = cells.f[n]; - if (features[ocean].type !== "ocean") continue; // not an ocean + if (features[ocean].type !== 'ocean') continue; // not an ocean removeLake(c, lake, ocean); break check_neighbours; } @@ -871,40 +894,40 @@ function openNearSeaLakes() { cells.c[threshold].forEach(function (c) { if (cells.h[c] >= 20) cells.t[c] = 1; // mark as coastline }); - features[lake].type = "ocean"; // mark former lake as ocean + features[lake].type = 'ocean'; // mark former lake as ocean } - TIME && console.timeEnd("openLakes"); + TIME && console.timeEnd('openLakes'); } // define map size and position based on template and random factor function defineMapSize() { const [size, latitude] = getSizeAndLatitude(); - const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options - if (randomize || !locked("mapSize")) mapSizeOutput.value = mapSizeInput.value = size; - if (randomize || !locked("latitude")) latitudeOutput.value = latitudeInput.value = latitude; + const randomize = new URL(window.location.href).searchParams.get('options') === 'default'; // ignore stored options + if (randomize || !locked('mapSize')) mapSizeOutput.value = mapSizeInput.value = size; + if (randomize || !locked('latitude')) latitudeOutput.value = latitudeInput.value = latitude; function getSizeAndLatitude() { - const template = document.getElementById("templateInput").value; // heightmap template - const part = grid.features.some(f => f.land && f.border); // if land goes over map borders + const template = document.getElementById('templateInput').value; // heightmap template + const part = grid.features.some((f) => f.land && f.border); // if land goes over map borders const max = part ? 80 : 100; // max size const lat = () => gauss(P(0.5) ? 40 : 60, 15, 25, 75); // latiture shift if (!part) { - if (template === "Pangea") return [100, 50]; - if (template === "Shattered" && P(0.7)) return [100, 50]; - if (template === "Continents" && P(0.5)) return [100, 50]; - if (template === "Archipelago" && P(0.35)) return [100, 50]; - if (template === "High Island" && P(0.25)) return [100, 50]; - if (template === "Low Island" && P(0.1)) return [100, 50]; + if (template === 'Pangea') return [100, 50]; + if (template === 'Shattered' && P(0.7)) return [100, 50]; + if (template === 'Continents' && P(0.5)) return [100, 50]; + if (template === 'Archipelago' && P(0.35)) return [100, 50]; + if (template === 'High Island' && P(0.25)) return [100, 50]; + if (template === 'Low Island' && P(0.1)) return [100, 50]; } - if (template === "Pangea") return [gauss(70, 20, 30, max), lat()]; - if (template === "Volcano") return [gauss(20, 20, 10, max), lat()]; - if (template === "Mediterranean") return [gauss(25, 30, 15, 80), lat()]; - if (template === "Peninsula") return [gauss(15, 15, 5, 80), lat()]; - if (template === "Isthmus") return [gauss(15, 20, 3, 80), lat()]; - if (template === "Atoll") return [gauss(5, 10, 2, max), lat()]; + if (template === 'Pangea') return [gauss(70, 20, 30, max), lat()]; + if (template === 'Volcano') return [gauss(20, 20, 10, max), lat()]; + if (template === 'Mediterranean') return [gauss(25, 30, 15, 80), lat()]; + if (template === 'Peninsula') return [gauss(15, 15, 5, 80), lat()]; + if (template === 'Isthmus') return [gauss(15, 20, 3, 80), lat()]; + if (template === 'Atoll') return [gauss(5, 10, 2, max), lat()]; return [gauss(30, 20, 15, max), lat()]; // Continents, Archipelago, High Island, Low Island } @@ -912,8 +935,8 @@ function defineMapSize() { // calculate map position on globe function calculateMapCoordinates() { - const size = +document.getElementById("mapSizeOutput").value; - const latShift = +document.getElementById("latitudeOutput").value; + const size = +document.getElementById('mapSizeOutput').value; + const latShift = +document.getElementById('latitudeOutput').value; const latT = (size / 100) * 180; const latN = 90 - ((180 - latT) * latShift) / 100; @@ -925,7 +948,7 @@ function calculateMapCoordinates() { // temperature model function calculateTemperatures() { - TIME && console.time("calculateTemperatures"); + TIME && console.time('calculateTemperatures'); const cells = grid.cells; cells.temp = new Int8Array(cells.i.length); // temperature array @@ -951,13 +974,13 @@ function calculateTemperatures() { return rn((height / 1000) * 6.5); } - TIME && console.timeEnd("calculateTemperatures"); + TIME && console.timeEnd('calculateTemperatures'); } // simplest precipitation model function generatePrecipitation() { - TIME && console.time("generatePrecipitation"); - prec.selectAll("*").remove(); + TIME && console.time('generatePrecipitation'); + prec.selectAll('*').remove(); const cells = grid.cells; cells.prec = new Uint8Array(cells.i.length); // precipitation array const modifier = precInput.value / 100; // user's input @@ -1050,53 +1073,53 @@ function generatePrecipitation() { } void (function drawWindDirection() { - const wind = prec.append("g").attr("id", "wind"); + const wind = prec.append('g').attr('id', 'wind'); d3.range(0, 6).forEach(function (t) { if (westerly.length > 1) { - const west = westerly.filter(w => w[2] === t); + const west = westerly.filter((w) => w[2] === t); if (west && west.length > 3) { const from = west[0][0], to = west[west.length - 1][0]; const y = (grid.points[from][1] + grid.points[to][1]) / 2; - wind.append("text").attr("x", 20).attr("y", y).text("\u21C9"); + wind.append('text').attr('x', 20).attr('y', y).text('\u21C9'); } } if (easterly.length > 1) { - const east = easterly.filter(w => w[2] === t); + const east = easterly.filter((w) => w[2] === t); if (east && east.length > 3) { const from = east[0][0], to = east[east.length - 1][0]; const y = (grid.points[from][1] + grid.points[to][1]) / 2; wind - .append("text") - .attr("x", graphWidth - 52) - .attr("y", y) - .text("\u21C7"); + .append('text') + .attr('x', graphWidth - 52) + .attr('y', y) + .text('\u21C7'); } } }); if (northerly) wind - .append("text") - .attr("x", graphWidth / 2) - .attr("y", 42) - .text("\u21CA"); + .append('text') + .attr('x', graphWidth / 2) + .attr('y', 42) + .text('\u21CA'); if (southerly) wind - .append("text") - .attr("x", graphWidth / 2) - .attr("y", graphHeight - 20) - .text("\u21C8"); + .append('text') + .attr('x', graphWidth / 2) + .attr('y', graphHeight - 20) + .text('\u21C8'); })(); - TIME && console.timeEnd("generatePrecipitation"); + TIME && console.timeEnd('generatePrecipitation'); } // recalculate Voronoi Graph to pack cells function reGraph() { - TIME && console.time("reGraph"); + TIME && console.time('reGraph'); let {cells, points, features} = grid; const newCells = {p: [], g: [], h: []}; // to store new data const spacing2 = grid.spacing ** 2; @@ -1105,7 +1128,7 @@ function reGraph() { const height = cells.h[i]; const type = cells.t[i]; if (height < 20 && type !== -1 && type !== -2) continue; // exclude all deep ocean points - if (type === -2 && (i % 4 === 0 || features[cells.f[i]].type === "lake")) continue; // exclude non-coastal lake points + if (type === -2 && (i % 4 === 0 || features[cells.f[i]].type === 'lake')) continue; // exclude non-coastal lake points const [x, y] = points[i]; addNewPoint(i, x, y, height); @@ -1139,14 +1162,14 @@ function reGraph() { cells.q = d3.quadtree(cells.p.map((p, d) => [p[0], p[1], d])); // points quadtree for fast search cells.h = new Uint8Array(newCells.h); // heights cells.area = new Uint16Array(cells.i.length); // cell area - cells.i.forEach(i => (cells.area[i] = Math.abs(d3.polygonArea(getPackPolygon(i))))); + cells.i.forEach((i) => (cells.area[i] = Math.abs(d3.polygonArea(getPackPolygon(i))))); - TIME && console.timeEnd("reGraph"); + TIME && console.timeEnd('reGraph'); } // Detect and draw the coasline function drawCoastline() { - TIME && console.time("drawCoastline"); + TIME && console.time('drawCoastline'); reMarkFeatures(); const cells = pack.cells, @@ -1155,11 +1178,11 @@ function drawCoastline() { features = pack.features; const used = new Uint8Array(features.length); // store conneted features const largestLand = d3.scan( - features.map(f => (f.land ? f.cells : 0)), + features.map((f) => (f.land ? f.cells : 0)), (a, b) => b - a ); - const landMask = defs.select("#land"); - const waterMask = defs.select("#water"); + const landMask = defs.select('#land'); + const waterMask = defs.select('#water'); lineGen.curve(d3.curveBasisClosed); for (const i of cells.i) { @@ -1167,20 +1190,20 @@ function drawCoastline() { if (!startFromEdge && cells.t[i] !== -1 && cells.t[i] !== 1) continue; // non-edge cell const f = cells.f[i]; if (used[f]) continue; // already connected - if (features[f].type === "ocean") continue; // ocean cell + if (features[f].type === 'ocean') continue; // ocean cell - const type = features[f].type === "lake" ? 1 : -1; // type value to search for + const type = features[f].type === 'lake' ? 1 : -1; // type value to search for const start = findStart(i, type); if (start === -1) continue; // cannot start here let vchain = connectVertices(start, type); - if (features[f].type === "lake") relax(vchain, 1.2); + if (features[f].type === 'lake') relax(vchain, 1.2); used[f] = 1; let points = clipPoly( - vchain.map(v => vertices.p[v]), + vchain.map((v) => vertices.p[v]), 1 ); const area = d3.polygonArea(points); // area with lakes/islands - if (area > 0 && features[f].type === "lake") { + if (area > 0 && features[f].type === 'lake') { points = points.reverse(); vchain = vchain.reverse(); } @@ -1189,37 +1212,37 @@ function drawCoastline() { features[f].vertices = vchain; const path = round(lineGen(points)); - if (features[f].type === "lake") { + if (features[f].type === 'lake') { landMask - .append("path") - .attr("d", path) - .attr("fill", "black") - .attr("id", "land_" + f); + .append('path') + .attr('d', path) + .attr('fill', 'black') + .attr('id', 'land_' + f); // waterMask.append("path").attr("d", path).attr("fill", "white").attr("id", "water_"+id); // uncomment to show over lakes lakes - .select("#freshwater") - .append("path") - .attr("d", path) - .attr("id", "lake_" + f) - .attr("data-f", f); // draw the lake + .select('#freshwater') + .append('path') + .attr('d', path) + .attr('id', 'lake_' + f) + .attr('data-f', f); // draw the lake } else { landMask - .append("path") - .attr("d", path) - .attr("fill", "white") - .attr("id", "land_" + f); + .append('path') + .attr('d', path) + .attr('fill', 'white') + .attr('id', 'land_' + f); waterMask - .append("path") - .attr("d", path) - .attr("fill", "black") - .attr("id", "water_" + f); - const g = features[f].group === "lake_island" ? "lake_island" : "sea_island"; + .append('path') + .attr('d', path) + .attr('fill', 'black') + .attr('id', 'water_' + f); + const g = features[f].group === 'lake_island' ? 'lake_island' : 'sea_island'; coastline - .select("#" + g) - .append("path") - .attr("d", path) - .attr("id", "island_" + f) - .attr("data-f", f); // draw the coastline + .select('#' + g) + .append('path') + .attr('d', path) + .attr('id', 'island_' + f) + .attr('data-f', f); // draw the coastline } // draw ruler to cover the biggest land piece @@ -1232,8 +1255,8 @@ function drawCoastline() { // find cell vertex to start path detection function findStart(i, t) { - if (t === -1 && cells.b[i]) return cells.v[i].find(v => vertices.c[v].some(c => c >= n)); // map border cell - const filtered = cells.c[i].filter(c => cells.t[c] === t); + if (t === -1 && cells.b[i]) return cells.v[i].find((v) => vertices.c[v].some((c) => c >= n)); // map border cell + const filtered = cells.c[i].filter((c) => cells.t[c] === t); const index = cells.c[i].indexOf(d3.min(filtered)); return index === -1 ? index : cells.v[i][index]; } @@ -1253,7 +1276,7 @@ function drawCoastline() { else if (v[1] !== prev && c1 !== c2) current = v[1]; else if (v[2] !== prev && c0 !== c2) current = v[2]; if (current === chain[chain.length - 1]) { - ERROR && console.error("Next vertex is not found"); + ERROR && console.error('Next vertex is not found'); break; } } @@ -1280,12 +1303,12 @@ function drawCoastline() { } } - TIME && console.timeEnd("drawCoastline"); + TIME && console.timeEnd('drawCoastline'); } // Re-mark features (ocean, lakes, islands) function reMarkFeatures() { - TIME && console.time("reMarkFeatures"); + TIME && console.time('reMarkFeatures'); const cells = pack.cells, features = (pack.features = [0]); cells.f = new Uint16Array(cells.i.length); // cell feature number @@ -1322,36 +1345,36 @@ function reMarkFeatures() { }); } - const type = land ? "island" : border ? "ocean" : "lake"; + const type = land ? 'island' : border ? 'ocean' : 'lake'; let group; - if (type === "ocean") group = defineOceanGroup(cellNumber); - else if (type === "island") group = defineIslandGroup(start, cellNumber); + if (type === 'ocean') group = defineOceanGroup(cellNumber); + else if (type === 'island') group = defineIslandGroup(start, cellNumber); features.push({i, land, border, type, cells: cellNumber, firstCell: start, group}); - queue[0] = cells.f.findIndex(f => !f); // find unmarked cell + queue[0] = cells.f.findIndex((f) => !f); // find unmarked cell } // markupPackLand markup(pack.cells, 3, 1, 0); function defineOceanGroup(number) { - if (number > grid.cells.i.length / 25) return "ocean"; - if (number > grid.cells.i.length / 100) return "sea"; - return "gulf"; + if (number > grid.cells.i.length / 25) return 'ocean'; + if (number > grid.cells.i.length / 100) return 'sea'; + return 'gulf'; } function defineIslandGroup(cell, number) { - if (cell && features[cells.f[cell - 1]].type === "lake") return "lake_island"; - if (number > grid.cells.i.length / 10) return "continent"; - if (number > grid.cells.i.length / 1000) return "island"; - return "isle"; + if (cell && features[cells.f[cell - 1]].type === 'lake') return 'lake_island'; + if (number > grid.cells.i.length / 10) return 'continent'; + if (number > grid.cells.i.length / 1000) return 'island'; + return 'isle'; } - TIME && console.timeEnd("reMarkFeatures"); + TIME && console.timeEnd('reMarkFeatures'); } // assign biome id for each cell function defineBiomes() { - TIME && console.time("defineBiomes"); + TIME && console.time('defineBiomes'); const cells = pack.cells, f = pack.features, temp = grid.cells.temp, @@ -1370,12 +1393,12 @@ function defineBiomes() { if (cells.r[i]) moist += Math.max(cells.fl[i] / 20, 2); const n = cells.c[i] .filter(isLand) - .map(c => prec[cells.g[c]]) + .map((c) => prec[cells.g[c]]) .concat([moist]); return rn(4 + d3.mean(n)); } - TIME && console.timeEnd("defineBiomes"); + TIME && console.timeEnd('defineBiomes'); } // assign biome id to a cell @@ -1390,12 +1413,12 @@ function getBiomeId(moisture, temperature, height) { // assess cells suitability to calculate population and rand cells for culture center and burgs placement function rankCells() { - TIME && console.time("rankCells"); + TIME && console.time('rankCells'); const {cells, features} = pack; cells.s = new Int16Array(cells.i.length); // cell suitability array cells.pop = new Float32Array(cells.i.length); // cell population array - const flMean = d3.median(cells.fl.filter(f => f)) || 0, + const flMean = d3.median(cells.fl.filter((f) => f)) || 0, flMax = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux const areaMean = d3.mean(cells.area); // to adjust population by cell area @@ -1409,13 +1432,13 @@ function rankCells() { if (cells.t[i] === 1) { if (cells.r[i]) s += 15; // estuary is valued const feature = features[cells.f[cells.haven[i]]]; - if (feature.type === "lake") { - if (feature.group === "freshwater") s += 30; - else if (feature.group == "salt") s += 10; - else if (feature.group == "frozen") s += 1; - else if (feature.group == "dry") s -= 5; - else if (feature.group == "sinkhole") s -= 5; - else if (feature.group == "lava") s -= 30; + if (feature.type === 'lake') { + if (feature.group === 'freshwater') s += 30; + else if (feature.group == 'salt') s += 10; + else if (feature.group == 'frozen') s += 1; + else if (feature.group == 'dry') s -= 5; + else if (feature.group == 'sinkhole') s -= 5; + else if (feature.group == 'lava') s -= 30; } else { s += 5; // ocean coast is valued if (cells.harbor[i] === 1) s += 20; // safe sea harbor is valued @@ -1427,30 +1450,30 @@ function rankCells() { cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / areaMean : 0; } - TIME && console.timeEnd("rankCells"); + TIME && console.timeEnd('rankCells'); } // generate some markers function addMarkers(number = 1) { if (!number) return; - TIME && console.time("addMarkers"); + TIME && console.time('addMarkers'); const cells = pack.cells, states = pack.states; void (function addVolcanoes() { let mounts = Array.from(cells.i) - .filter(i => cells.h[i] > 70) + .filter((i) => cells.h[i] > 70) .sort((a, b) => cells.h[b] - cells.h[a]); let count = mounts.length < 10 ? 0 : Math.ceil((mounts.length / 300) * number); - if (count) addMarker("volcano", "🌋", 52, 50, 13); + if (count) addMarker('volcano', '🌋', 52, 50, 13); while (count && mounts.length) { const cell = mounts.splice(biased(0, mounts.length - 1, 5), 1); const x = cells.p[cell][0], y = cells.p[cell][1]; - const id = appendMarker(cell, "volcano"); + const id = appendMarker(cell, 'volcano'); const proper = Names.getCulture(cells.culture[cell]); - const name = P(0.3) ? "Mount " + proper : Math.random() > 0.3 ? proper + " Volcano" : proper; + const name = P(0.3) ? 'Mount ' + proper : Math.random() > 0.3 ? proper + ' Volcano' : proper; notes.push({id, name, legend: `Active volcano. Height: ${getFriendlyHeight([x, y])}`}); count--; } @@ -1458,32 +1481,32 @@ function addMarkers(number = 1) { void (function addHotSprings() { let springs = Array.from(cells.i) - .filter(i => cells.h[i] > 50) + .filter((i) => cells.h[i] > 50) .sort((a, b) => cells.h[b] - cells.h[a]); let count = springs.length < 30 ? 0 : Math.ceil((springs.length / 1000) * number); - if (count) addMarker("hot_springs", "♨️", 50, 52, 12.5); + if (count) addMarker('hot_springs', '♨️', 50, 52, 12.5); while (count && springs.length) { const cell = springs.splice(biased(1, springs.length - 1, 3), 1); - const id = appendMarker(cell, "hot_springs"); + const id = appendMarker(cell, 'hot_springs'); const proper = Names.getCulture(cells.culture[cell]); const temp = convertTemperature(gauss(30, 15, 20, 100)); - notes.push({id, name: proper + " Hot Springs", legend: `A hot springs area. Temperature: ${temp}`}); + notes.push({id, name: proper + ' Hot Springs', legend: `A hot springs area. Temperature: ${temp}`}); count--; } })(); void (function addMines() { - let hills = Array.from(cells.i).filter(i => cells.h[i] > 47 && cells.burg[i]); + let hills = Array.from(cells.i).filter((i) => cells.h[i] > 47 && cells.burg[i]); let count = !hills.length ? 0 : Math.ceil((hills.length / 7) * number); if (!count) return; - addMarker("mine", "⛏️", 48, 50, 13.5); + addMarker('mine', '⛏️', 48, 50, 13.5); const resources = {salt: 5, gold: 2, silver: 4, copper: 2, iron: 3, lead: 1, tin: 1}; while (count && hills.length) { const cell = hills.splice(Math.floor(Math.random() * hills.length), 1); - const id = appendMarker(cell, "mine"); + const id = appendMarker(cell, 'mine'); const resource = rw(resources); const burg = pack.burgs[cells.burg[cell]]; const name = `${burg.name} — ${resource} mining town`; @@ -1495,22 +1518,22 @@ function addMarkers(number = 1) { })(); void (function addBridges() { - const meanRoad = d3.mean(cells.road.filter(r => r)); - const meanFlux = d3.mean(cells.fl.filter(fl => fl)); + const meanRoad = d3.mean(cells.road.filter((r) => r)); + const meanFlux = d3.mean(cells.fl.filter((fl) => fl)); let bridges = Array.from(cells.i) - .filter(i => cells.burg[i] && cells.h[i] >= 20 && cells.r[i] && cells.fl[i] > meanFlux && cells.road[i] > meanRoad) + .filter((i) => cells.burg[i] && cells.h[i] >= 20 && cells.r[i] && cells.fl[i] > meanFlux && cells.road[i] > meanRoad) .sort((a, b) => cells.road[b] + cells.fl[b] / 10 - (cells.road[a] + cells.fl[a] / 10)); let count = !bridges.length ? 0 : Math.ceil((bridges.length / 12) * number); - if (count) addMarker("bridge", "🌉", 50, 50, 14); + if (count) addMarker('bridge', '🌉', 50, 50, 14); while (count && bridges.length) { const cell = bridges.splice(0, 1); - const id = appendMarker(cell, "bridge"); + const id = appendMarker(cell, 'bridge'); const burg = pack.burgs[cells.burg[cell]]; - const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); - const riverName = river ? `${river.name} ${river.type}` : "river"; + const river = pack.rivers.find((r) => r.i === pack.cells.r[cell]); + const riverName = river ? `${river.name} ${river.type}` : 'river'; const name = river && P(0.2) ? river.name : burg.name; notes.push({id, name: `${name} Bridge`, legend: `A stone bridge over the ${riverName} near ${burg.name}`}); count--; @@ -1519,63 +1542,153 @@ function addMarkers(number = 1) { void (function addInns() { const maxRoad = d3.max(cells.road) * 0.9; - let taverns = Array.from(cells.i).filter(i => cells.crossroad[i] && cells.h[i] >= 20 && cells.road[i] > maxRoad); + let taverns = Array.from(cells.i).filter((i) => cells.crossroad[i] && cells.h[i] >= 20 && cells.road[i] > maxRoad); if (!taverns.length) return; const count = Math.ceil(4 * number); - addMarker("inn", "🍻", 50, 50, 14.5); + addMarker('inn', '🍻', 50, 50, 14.5); - const color = ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"]; - const animal = ["Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Wolf", "Wolverine", "Camel", "Falcon", "Hound", "Ox"]; - const adj = ["New", "Good", "High", "Old", "Great", "Big", "Major", "Happy", "Main", "Huge", "Far", "Beautiful", "Fair", "Prime", "Ancient", "Golden", "Proud", "Lucky", "Fat", "Honest", "Giant", "Distant", "Friendly", "Loud", "Hungry", "Magical", "Superior", "Peaceful", "Frozen", "Divine", "Favorable", "Brave", "Sunny", "Flying"]; + const color = ['Dark', 'Light', 'Bright', 'Golden', 'White', 'Black', 'Red', 'Pink', 'Purple', 'Blue', 'Green', 'Yellow', 'Amber', 'Orange', 'Brown', 'Grey']; + const animal = [ + 'Antelope', + 'Ape', + 'Badger', + 'Bear', + 'Beaver', + 'Bison', + 'Boar', + 'Buffalo', + 'Cat', + 'Crane', + 'Crocodile', + 'Crow', + 'Deer', + 'Dog', + 'Eagle', + 'Elk', + 'Fox', + 'Goat', + 'Goose', + 'Hare', + 'Hawk', + 'Heron', + 'Horse', + 'Hyena', + 'Ibis', + 'Jackal', + 'Jaguar', + 'Lark', + 'Leopard', + 'Lion', + 'Mantis', + 'Marten', + 'Moose', + 'Mule', + 'Narwhal', + 'Owl', + 'Panther', + 'Rat', + 'Raven', + 'Rook', + 'Scorpion', + 'Shark', + 'Sheep', + 'Snake', + 'Spider', + 'Swan', + 'Tiger', + 'Turtle', + 'Wolf', + 'Wolverine', + 'Camel', + 'Falcon', + 'Hound', + 'Ox' + ]; + const adj = [ + 'New', + 'Good', + 'High', + 'Old', + 'Great', + 'Big', + 'Major', + 'Happy', + 'Main', + 'Huge', + 'Far', + 'Beautiful', + 'Fair', + 'Prime', + 'Ancient', + 'Golden', + 'Proud', + 'Lucky', + 'Fat', + 'Honest', + 'Giant', + 'Distant', + 'Friendly', + 'Loud', + 'Hungry', + 'Magical', + 'Superior', + 'Peaceful', + 'Frozen', + 'Divine', + 'Favorable', + 'Brave', + 'Sunny', + 'Flying' + ]; for (let i = 0; i < taverns.length && i < count; i++) { const cell = taverns.splice(Math.floor(Math.random() * taverns.length), 1); - const id = appendMarker(cell, "inn"); - const type = P(0.3) ? "inn" : "tavern"; - const name = P(0.5) ? ra(color) + " " + ra(animal) : P(0.6) ? ra(adj) + " " + ra(animal) : ra(adj) + " " + capitalize(type); - notes.push({id, name: "The " + name, legend: `A big and famous roadside ${type}`}); + const id = appendMarker(cell, 'inn'); + const type = P(0.3) ? 'inn' : 'tavern'; + const name = P(0.5) ? ra(color) + ' ' + ra(animal) : P(0.6) ? ra(adj) + ' ' + ra(animal) : ra(adj) + ' ' + capitalize(type); + notes.push({id, name: 'The ' + name, legend: `A big and famous roadside ${type}`}); } })(); void (function addLighthouses() { - const lands = cells.i.filter(i => cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.road[c])); - const lighthouses = Array.from(lands).map(i => [i, cells.v[i][cells.c[i].findIndex(c => cells.h[c] < 20 && cells.road[c])]]); - if (lighthouses.length) addMarker("lighthouse", "🚨", 50, 50, 16); + const lands = cells.i.filter((i) => cells.harbor[i] > 6 && cells.c[i].some((c) => cells.h[c] < 20 && cells.road[c])); + const lighthouses = Array.from(lands).map((i) => [i, cells.v[i][cells.c[i].findIndex((c) => cells.h[c] < 20 && cells.road[c])]]); + if (lighthouses.length) addMarker('lighthouse', '🚨', 50, 50, 16); const count = Math.ceil(4 * number); for (let i = 0; i < lighthouses.length && i < count; i++) { const cell = lighthouses[i][0], vertex = lighthouses[i][1]; - const id = appendMarker(cell, "lighthouse"); + const id = appendMarker(cell, 'lighthouse'); const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); - notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to keep the navigation safe`}); + notes.push({id, name: getAdjective(proper) + ' Lighthouse' + name, legend: `A lighthouse to keep the navigation safe`}); } })(); void (function addWaterfalls() { - const waterfalls = cells.i.filter(i => cells.r[i] && cells.h[i] > 70); - if (waterfalls.length) addMarker("waterfall", "⟱", 50, 54, 16.5); + const waterfalls = cells.i.filter((i) => cells.r[i] && cells.h[i] > 70); + if (waterfalls.length) addMarker('waterfall', '⟱', 50, 54, 16.5); const count = Math.ceil(3 * number); for (let i = 0; i < waterfalls.length && i < count; i++) { const cell = waterfalls[i]; - const id = appendMarker(cell, "waterfall"); + const id = appendMarker(cell, 'waterfall'); const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); - notes.push({id, name: getAdjective(proper) + " Waterfall" + name, legend: `An extremely beautiful waterfall`}); + notes.push({id, name: getAdjective(proper) + ' Waterfall' + name, legend: `An extremely beautiful waterfall`}); } })(); void (function addBattlefields() { - let battlefields = Array.from(cells.i).filter(i => cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25); + let battlefields = Array.from(cells.i).filter((i) => cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25); let count = battlefields.length < 100 ? 0 : Math.ceil((battlefields.length / 500) * number); - if (count) addMarker("battlefield", "⚔️", 50, 52, 12); + if (count) addMarker('battlefield', '⚔️', 50, 52, 12); while (count && battlefields.length) { const cell = battlefields.splice(Math.floor(Math.random() * battlefields.length), 1); - const id = appendMarker(cell, "battlefield"); + const id = appendMarker(cell, 'battlefield'); const campaign = ra(states[cells.state[cell]].campaigns); const date = generateDate(campaign.start, campaign.end); - const name = Names.getCulture(cells.culture[cell]) + " Battlefield"; + const name = Names.getCulture(cells.culture[cell]) + ' Battlefield'; const legend = `A historical battle of the ${campaign.name}. \r\nDate: ${date} ${options.era}`; notes.push({id, name, legend}); count--; @@ -1583,55 +1696,55 @@ function addMarkers(number = 1) { })(); function addMarker(id, icon, x, y, size) { - const markers = svg.select("#defs-markers"); - if (markers.select("#marker_" + id).size()) return; + const markers = svg.select('#defs-markers'); + if (markers.select('#marker_' + id).size()) return; const symbol = markers - .append("symbol") - .attr("id", "marker_" + id) - .attr("viewBox", "0 0 30 30"); - symbol.append("path").attr("d", "M6,19 l9,10 L24,19").attr("fill", "#000000").attr("stroke", "none"); - symbol.append("circle").attr("cx", 15).attr("cy", 15).attr("r", 10).attr("fill", "#ffffff").attr("stroke", "#000000").attr("stroke-width", 1); + .append('symbol') + .attr('id', 'marker_' + id) + .attr('viewBox', '0 0 30 30'); + symbol.append('path').attr('d', 'M6,19 l9,10 L24,19').attr('fill', '#000000').attr('stroke', 'none'); + symbol.append('circle').attr('cx', 15).attr('cy', 15).attr('r', 10).attr('fill', '#ffffff').attr('stroke', '#000000').attr('stroke-width', 1); symbol - .append("text") - .attr("x", x + "%") - .attr("y", y + "%") - .attr("fill", "#000000") - .attr("stroke", "#3200ff") - .attr("stroke-width", 0) - .attr("font-size", size + "px") - .attr("dominant-baseline", "central") + .append('text') + .attr('x', x + '%') + .attr('y', y + '%') + .attr('fill', '#000000') + .attr('stroke', '#3200ff') + .attr('stroke-width', 0) + .attr('font-size', size + 'px') + .attr('dominant-baseline', 'central') .text(icon); } function appendMarker(cell, type) { const x = cells.p[cell][0], y = cells.p[cell][1]; - const id = getNextId("markerElement"); - const name = "#marker_" + type; + const id = getNextId('markerElement'); + const name = '#marker_' + type; markers - .append("use") - .attr("id", id) - .attr("xlink:href", name) - .attr("data-id", name) - .attr("data-x", x) - .attr("data-y", y) - .attr("x", x - 15) - .attr("y", y - 30) - .attr("data-size", 1) - .attr("width", 30) - .attr("height", 30); + .append('use') + .attr('id', id) + .attr('xlink:href', name) + .attr('data-id', name) + .attr('data-x', x) + .attr('data-y', y) + .attr('x', x - 15) + .attr('y', y - 30) + .attr('data-size', 1) + .attr('width', 30) + .attr('height', 30); return id; } - TIME && console.timeEnd("addMarkers"); + TIME && console.timeEnd('addMarkers'); } // regenerate some zones function addZones(number = 1) { - TIME && console.time("addZones"); + TIME && console.time('addZones'); const data = [], cells = pack.cells, states = pack.states, @@ -1651,13 +1764,13 @@ function addZones(number = 1) { for (let i = 0; i < rn(Math.random() * 1.2 * number); i++) addTsunami(); // tsunami starting near coast function addInvasion() { - const atWar = states.filter(s => s.diplomacy && s.diplomacy.some(d => d === "Enemy")); + const atWar = states.filter((s) => s.diplomacy && s.diplomacy.some((d) => d === 'Enemy')); if (!atWar.length) return; const invader = ra(atWar); - const target = invader.diplomacy.findIndex(d => d === "Enemy"); + const target = invader.diplomacy.findIndex((d) => d === 'Enemy'); - const cell = ra(cells.i.filter(i => cells.state[i] === target && cells.c[i].some(c => cells.state[c] === invader.i))); + const cell = ra(cells.i.filter((i) => cells.state[i] === target && cells.c[i].some((c) => cells.state[c] === invader.i))); if (!cell) return; const cellsArray = [], @@ -1669,7 +1782,7 @@ function addZones(number = 1) { cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e]) return; if (cells.state[e] !== target) return; used[e] = 1; @@ -1678,16 +1791,16 @@ function addZones(number = 1) { } const invasion = rw({Invasion: 4, Occupation: 3, Raid: 2, Conquest: 2, Subjugation: 1, Foray: 1, Skirmishes: 1, Incursion: 2, Pillaging: 1, Intervention: 1}); - const name = getAdjective(invader.name) + " " + invasion; - data.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"}); + const name = getAdjective(invader.name) + ' ' + invasion; + data.push({name, type: 'Invasion', cells: cellsArray, fill: 'url(#hatch1)'}); } function addRebels() { - const state = ra(states.filter(s => s.i && s.neighbors.some(n => n))); + const state = ra(states.filter((s) => s.i && s.neighbors.some((n) => n))); if (!state) return; - const neib = ra(state.neighbors.filter(n => n)); - const cell = cells.i.find(i => cells.state[i] === state.i && cells.c[i].some(c => cells.state[c] === neib)); + const neib = ra(state.neighbors.filter((n) => n)); + const cell = cells.i.find((i) => cells.state[i] === state.i && cells.c[i].some((c) => cells.state[c] === neib)); const cellsArray = [], queue = [cell], power = rand(10, 30); @@ -1697,25 +1810,25 @@ function addZones(number = 1) { cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e]) return; if (cells.state[e] !== state.i) return; used[e] = 1; - if (e % 4 !== 0 && !cells.c[e].some(c => cells.state[c] === neib)) return; + if (e % 4 !== 0 && !cells.c[e].some((c) => cells.state[c] === neib)) return; queue.push(e); }); } const rebels = rw({Rebels: 5, Insurgents: 2, Mutineers: 1, Rioters: 1, Separatists: 1, Secessionists: 1, Insurrection: 2, Rebellion: 1, Conspiracy: 2}); - const name = getAdjective(states[neib].name) + " " + rebels; - data.push({name, type: "Rebels", cells: cellsArray, fill: "url(#hatch3)"}); + const name = getAdjective(states[neib].name) + ' ' + rebels; + data.push({name, type: 'Rebels', cells: cellsArray, fill: 'url(#hatch3)'}); } function addProselytism() { - const organized = ra(pack.religions.filter(r => r.type === "Organized")); + const organized = ra(pack.religions.filter((r) => r.type === 'Organized')); if (!organized) return; - const cell = ra(cells.i.filter(i => cells.religion[i] && cells.religion[i] !== organized.i && cells.c[i].some(c => cells.religion[c] === organized.i))); + const cell = ra(cells.i.filter((i) => cells.religion[i] && cells.religion[i] !== organized.i && cells.c[i].some((c) => cells.religion[c] === organized.i))); if (!cell) return; const target = cells.religion[cell]; const cellsArray = [], @@ -1727,7 +1840,7 @@ function addZones(number = 1) { cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e]) return; if (cells.religion[e] !== target) return; if (cells.h[e] < 20) return; @@ -1737,24 +1850,24 @@ function addZones(number = 1) { }); } - const name = getAdjective(organized.name.split(" ")[0]) + " Proselytism"; - data.push({name, type: "Proselytism", cells: cellsArray, fill: "url(#hatch6)"}); + const name = getAdjective(organized.name.split(' ')[0]) + ' Proselytism'; + data.push({name, type: 'Proselytism', cells: cellsArray, fill: 'url(#hatch6)'}); } function addCrusade() { - const heresy = ra(pack.religions.filter(r => r.type === "Heresy")); + const heresy = ra(pack.religions.filter((r) => r.type === 'Heresy')); if (!heresy) return; - const cellsArray = cells.i.filter(i => !used[i] && cells.religion[i] === heresy.i); + const cellsArray = cells.i.filter((i) => !used[i] && cells.religion[i] === heresy.i); if (!cellsArray.length) return; - cellsArray.forEach(i => (used[i] = 1)); + cellsArray.forEach((i) => (used[i] = 1)); - const name = getAdjective(heresy.name.split(" ")[0]) + " Crusade"; - data.push({name, type: "Crusade", cells: cellsArray, fill: "url(#hatch6)"}); + const name = getAdjective(heresy.name.split(' ')[0]) + ' Crusade'; + data.push({name, type: 'Crusade', cells: cellsArray, fill: 'url(#hatch6)'}); } function addDisease() { - const burg = ra(burgs.filter(b => !used[b.cell] && b.i && !b.removed)); // random burg + const burg = ra(burgs.filter((b) => !used[b.cell] && b.i && !b.removed)); // random burg if (!burg) return; const cellsArray = [], @@ -1781,17 +1894,17 @@ function addZones(number = 1) { }); } - const adjective = () => ra(["Great", "Silent", "Severe", "Blind", "Unknown", "Loud", "Deadly", "Burning", "Bloody", "Brutal", "Fatal"]); - const animal = () => ra(["Ape", "Bear", "Boar", "Cat", "Cow", "Dog", "Pig", "Fox", "Bird", "Horse", "Rat", "Raven", "Sheep", "Spider", "Wolf"]); - const color = () => ra(["Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"]); + const adjective = () => ra(['Great', 'Silent', 'Severe', 'Blind', 'Unknown', 'Loud', 'Deadly', 'Burning', 'Bloody', 'Brutal', 'Fatal']); + const animal = () => ra(['Ape', 'Bear', 'Boar', 'Cat', 'Cow', 'Dog', 'Pig', 'Fox', 'Bird', 'Horse', 'Rat', 'Raven', 'Sheep', 'Spider', 'Wolf']); + const color = () => ra(['Golden', 'White', 'Black', 'Red', 'Pink', 'Purple', 'Blue', 'Green', 'Yellow', 'Amber', 'Orange', 'Brown', 'Grey']); const type = rw({Fever: 5, Pestilence: 2, Flu: 2, Pox: 2, Smallpox: 2, Plague: 4, Cholera: 2, Dropsy: 1, Leprosy: 2}); - const name = rw({[color()]: 4, [animal()]: 2, [adjective()]: 1}) + " " + type; - data.push({name, type: "Disease", cells: cellsArray, fill: "url(#hatch12)"}); + const name = rw({[color()]: 4, [animal()]: 2, [adjective()]: 1}) + ' ' + type; + data.push({name, type: 'Disease', cells: cellsArray, fill: 'url(#hatch12)'}); } function addDisaster() { - const burg = ra(burgs.filter(b => !used[b.cell] && b.i && !b.removed)); // random burg + const burg = ra(burgs.filter((b) => !used[b.cell] && b.i && !b.removed)); // random burg if (!burg) return; const cellsArray = [], @@ -1818,22 +1931,22 @@ function addZones(number = 1) { } const type = rw({Famine: 5, Dearth: 1, Drought: 3, Earthquake: 3, Tornadoes: 1, Wildfires: 1}); - const name = getAdjective(burg.name) + " " + type; - data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"}); + const name = getAdjective(burg.name) + ' ' + type; + data.push({name, type: 'Disaster', cells: cellsArray, fill: 'url(#hatch5)'}); } function addEruption() { - const volcano = document.getElementById("markers").querySelector("use[data-id='#marker_volcano']"); + const volcano = document.getElementById('markers').querySelector("use[data-id='#marker_volcano']"); if (!volcano) return; const x = +volcano.dataset.x, y = +volcano.dataset.y, cell = findCell(x, y); const id = volcano.id; - const note = notes.filter(n => n.id === id); + const note = notes.filter((n) => n.id === id); - if (note[0]) note[0].legend = note[0].legend.replace("Active volcano", "Erupting volcano"); - const name = note[0] ? note[0].name.replace(" Volcano", "") + " Eruption" : "Volcano Eruption"; + if (note[0]) note[0].legend = note[0].legend.replace('Active volcano', 'Erupting volcano'); + const name = note[0] ? note[0].name.replace(' Volcano', '') + ' Eruption' : 'Volcano Eruption'; const cellsArray = [], queue = [cell], @@ -1843,18 +1956,18 @@ function addZones(number = 1) { const q = P(0.5) ? queue.shift() : queue.pop(); cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e] || cells.h[e] < 20) return; used[e] = 1; queue.push(e); }); } - data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch7)"}); + data.push({name, type: 'Disaster', cells: cellsArray, fill: 'url(#hatch7)'}); } function addAvalanche() { - const roads = cells.i.filter(i => !used[i] && cells.road[i] && cells.h[i] >= 70); + const roads = cells.i.filter((i) => !used[i] && cells.road[i] && cells.h[i] >= 70); if (!roads.length) return; const cell = +ra(roads); @@ -1866,7 +1979,7 @@ function addZones(number = 1) { const q = P(0.3) ? queue.shift() : queue.pop(); cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e] || cells.h[e] < 65) return; used[e] = 1; queue.push(e); @@ -1874,12 +1987,12 @@ function addZones(number = 1) { } const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); - const name = proper + " Avalanche"; - data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"}); + const name = proper + ' Avalanche'; + data.push({name, type: 'Disaster', cells: cellsArray, fill: 'url(#hatch5)'}); } function addFault() { - const elevated = cells.i.filter(i => !used[i] && cells.h[i] > 50 && cells.h[i] < 70); + const elevated = cells.i.filter((i) => !used[i] && cells.h[i] > 50 && cells.h[i] < 70); if (!elevated.length) return; const cell = ra(elevated); @@ -1891,7 +2004,7 @@ function addZones(number = 1) { const q = queue.pop(); if (cells.h[q] >= 20) cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e] || cells.r[e]) return; used[e] = 1; queue.push(e); @@ -1899,16 +2012,16 @@ function addZones(number = 1) { } const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); - const name = proper + " Fault"; - data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch2)"}); + const name = proper + ' Fault'; + data.push({name, type: 'Disaster', cells: cellsArray, fill: 'url(#hatch2)'}); } function addFlood() { - const fl = cells.fl.filter(fl => fl), + const fl = cells.fl.filter((fl) => fl), meanFlux = d3.mean(fl), maxFlux = d3.max(fl), flux = (maxFlux - meanFlux) / 2 + meanFlux; - const rivers = cells.i.filter(i => !used[i] && cells.h[i] < 50 && cells.r[i] && cells.fl[i] > flux && cells.burg[i]); + const rivers = cells.i.filter((i) => !used[i] && cells.h[i] < 50 && cells.r[i] && cells.fl[i] > flux && cells.burg[i]); if (!rivers.length) return; const cell = +ra(rivers), @@ -1922,19 +2035,19 @@ function addZones(number = 1) { cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e] || cells.h[e] < 20 || cells.r[e] !== river || cells.h[e] > 50 || cells.fl[e] < meanFlux) return; used[e] = 1; queue.push(e); }); } - const name = getAdjective(burgs[cells.burg[cell]].name) + " Flood"; - data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"}); + const name = getAdjective(burgs[cells.burg[cell]].name) + ' Flood'; + data.push({name, type: 'Disaster', cells: cellsArray, fill: 'url(#hatch13)'}); } function addTsunami() { - const coastal = cells.i.filter(i => !used[i] && cells.t[i] === -1 && pack.features[cells.f[i]].type !== "lake"); + const coastal = cells.i.filter((i) => !used[i] && cells.t[i] === -1 && pack.features[cells.f[i]].type !== 'lake'); if (!coastal.length) return; const cell = +ra(coastal); @@ -1947,48 +2060,48 @@ function addZones(number = 1) { if (cells.t[q] === 1) cellsArray.push(q); if (cellsArray.length > power) break; - cells.c[q].forEach(e => { + cells.c[q].forEach((e) => { if (used[e]) return; if (cells.t[e] > 2) return; - if (pack.features[cells.f[e]].type === "lake") return; + if (pack.features[cells.f[e]].type === 'lake') return; used[e] = 1; queue.push(e); }); } const proper = getAdjective(Names.getCultureShort(cells.culture[cell])); - const name = proper + " Tsunami"; - data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"}); + const name = proper + ' Tsunami'; + data.push({name, type: 'Disaster', cells: cellsArray, fill: 'url(#hatch13)'}); } void (function drawZones() { zones - .selectAll("g") + .selectAll('g') .data(data) .enter() - .append("g") - .attr("id", (d, i) => "zone" + i) - .attr("data-description", d => d.name) - .attr("data-type", d => d.type) - .attr("data-cells", d => d.cells.join(",")) - .attr("fill", d => d.fill) - .selectAll("polygon") - .data(d => d.cells) + .append('g') + .attr('id', (d, i) => 'zone' + i) + .attr('data-description', (d) => d.name) + .attr('data-type', (d) => d.type) + .attr('data-cells', (d) => d.cells.join(',')) + .attr('fill', (d) => d.fill) + .selectAll('polygon') + .data((d) => d.cells) .enter() - .append("polygon") - .attr("points", d => getPackPolygon(d)) - .attr("id", function (d) { - return this.parentNode.id + "_" + d; + .append('polygon') + .attr('points', (d) => getPackPolygon(d)) + .attr('id', function (d) { + return this.parentNode.id + '_' + d; }); })(); - TIME && console.timeEnd("addZones"); + TIME && console.timeEnd('addZones'); } // show map stats on generation complete function showStatistics() { const template = templateInput.value; - const templateRandom = locked("template") ? "" : "(random)"; + const templateRandom = locked('template') ? '' : '(random)'; const stats = ` Seed: ${seed} Canvas size: ${graphWidth}x${graphHeight} Template: ${template} ${templateRandom} @@ -2008,25 +2121,25 @@ function showStatistics() { } const regenerateMap = debounce(function () { - WARN && console.warn("Generate new random map"); - closeDialogs("#worldConfigurator, #options3d"); + WARN && console.warn('Generate new random map'); + closeDialogs('#worldConfigurator, #options3d'); customization = 0; undraw(); resetZoom(1000); generate(); restoreLayers(); if (ThreeD.options.isOn) ThreeD.redraw(); - if ($("#worldConfigurator").is(":visible")) editWorld(); + if ($('#worldConfigurator').is(':visible')) editWorld(); }, 500); // clear the map function undraw() { - viewbox.selectAll("path, circle, polygon, line, text, use, #zones > g, #armies > g, #ruler > g").remove(); + viewbox.selectAll('path, circle, polygon, line, text, use, #zones > g, #armies > g, #ruler > g').remove(); document - .getElementById("deftemp") - .querySelectorAll("path, clipPath, svg") - .forEach(el => el.remove()); - document.getElementById("coas").innerHTML = ""; // remove auto-generated emblems + .getElementById('deftemp') + .querySelectorAll('path, clipPath, svg') + .forEach((el) => el.remove()); + document.getElementById('coas').innerHTML = ''; // remove auto-generated emblems notes = []; rulers = new Rulers(); unfog(); diff --git a/modules/load.js b/modules/load.js index ca5bf23b..4b4f1a00 100644 --- a/modules/load.js +++ b/modules/load.js @@ -1,13 +1,13 @@ // Functions to save and load the map -"use strict"; +'use strict'; function quickLoad() { - ldb.get("lastMap", blob => { + ldb.get('lastMap', (blob) => { if (blob) { loadMapPrompt(blob); } else { - tip("No map stored. Save map to storage first", true, "error", 2000); - ERROR && console.error("No map stored"); + tip('No map stored. Save map to storage first', true, 'error', 2000); + ERROR && console.error('No map stored'); } }); } @@ -21,27 +21,27 @@ function loadMapPrompt(blob) { alertMessage.innerHTML = `Are you sure you want to load saved map?
All unsaved changes made to the current map will be lost`; - $("#alert").dialog({ + $('#alert').dialog({ resizable: false, - title: "Load saved map", + title: 'Load saved map', buttons: { Cancel: function () { - $(this).dialog("close"); + $(this).dialog('close'); }, Load: function () { loadLastSavedMap(); - $(this).dialog("close"); + $(this).dialog('close'); } } }); function loadLastSavedMap() { - WARN && console.warn("Load last saved map"); + WARN && console.warn('Load last saved map'); try { uploadMap(blob); } catch (error) { ERROR && console.error(error); - tip("Cannot load last saved map", true, "error", 2000); + tip('Cannot load last saved map', true, 'error', 2000); } } } @@ -52,20 +52,20 @@ function uploadMap(file, callback) { const fileReader = new FileReader(); fileReader.onload = function (fileLoadedEvent) { if (callback) callback(); - document.getElementById("coas").innerHTML = ""; // remove auto-generated emblems + document.getElementById('coas').innerHTML = ''; // remove auto-generated emblems const dataLoaded = fileLoadedEvent.target.result; - const data = dataLoaded.split("\r\n"); + const data = dataLoaded.split('\r\n'); - const mapVersion = data[0].split("|")[0] || data[0]; + const mapVersion = data[0].split('|')[0] || data[0]; if (mapVersion === version) { parseLoadedData(data); return; } - const archive = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "archived version"); + const archive = link('https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog', 'archived version'); const parsed = parseFloat(mapVersion); - let message = "", + let message = '', load = false; if (isNaN(parsed) || data.length < 26 || !data[5]) { message = `The file you are trying to load is outdated or not a valid .map file. @@ -79,19 +79,19 @@ function uploadMap(file, callback) {
Click OK to get map auto-updated. In case of issues please keep using an ${archive} of the Generator`; } alertMessage.innerHTML = message; - $("#alert").dialog({ - title: "Version conflict", - width: "38em", + $('#alert').dialog({ + title: 'Version conflict', + width: '38em', buttons: { OK: function () { - $(this).dialog("close"); + $(this).dialog('close'); if (load) parseLoadedData(data); } } }); }; - fileReader.readAsText(file, "UTF-8"); + fileReader.readAsText(file, 'UTF-8'); } function parseLoadedData(data) { @@ -101,11 +101,11 @@ function parseLoadedData(data) { customization = 0; if (customizationMenu.offsetParent) styleTab.click(); - const reliefIcons = document.getElementById("defs-relief").innerHTML; // save relief icons - const hatching = document.getElementById("hatching").cloneNode(true); // save hatching + const reliefIcons = document.getElementById('defs-relief').innerHTML; // save relief icons + const hatching = document.getElementById('hatching').cloneNode(true); // save hatching void (function parseParameters() { - const params = data[0].split("|"); + const params = data[0].split('|'); if (params[3]) { seed = params[3]; optionsSeed.value = seed; @@ -115,10 +115,10 @@ function parseLoadedData(data) { mapId = params[6] ? +params[6] : Date.now(); })(); - INFO && console.group("Loaded Map " + seed); + INFO && console.group('Loaded Map ' + seed); void (function parseSettings() { - const settings = data[1].split("|"); + const settings = data[1].split('|'); if (settings[0]) applyOption(distanceUnitInput, settings[0]); if (settings[1]) distanceScaleInput.value = distanceScaleOutput.value = settings[1]; if (settings[2]) areaUnit.value = settings[2]; @@ -148,11 +148,11 @@ function parseLoadedData(data) { if (data[4]) notes = JSON.parse(data[4]); if (data[33]) rulers.fromString(data[33]); - const biomes = data[3].split("|"); + const biomes = data[3].split('|'); biomesData = applyDefaultBiomesSystem(); - biomesData.color = biomes[0].split(","); - biomesData.habitability = biomes[1].split(",").map(h => +h); - biomesData.name = biomes[2].split(","); + biomesData.color = biomes[0].split(','); + biomesData.habitability = biomes[1].split(',').map((h) => +h); + biomesData.name = biomes[2].split(','); // push custom biomes if any for (let i = biomesData.i.length; i < biomesData.name.length; i++) { @@ -165,69 +165,69 @@ function parseLoadedData(data) { void (function replaceSVG() { svg.remove(); - document.body.insertAdjacentHTML("afterbegin", data[5]); + document.body.insertAdjacentHTML('afterbegin', data[5]); })(); void (function redefineElements() { - svg = d3.select("#map"); - defs = svg.select("#deftemp"); - viewbox = svg.select("#viewbox"); - scaleBar = svg.select("#scaleBar"); - legend = svg.select("#legend"); - ocean = viewbox.select("#ocean"); - oceanLayers = ocean.select("#oceanLayers"); - oceanPattern = ocean.select("#oceanPattern"); - lakes = viewbox.select("#lakes"); - landmass = viewbox.select("#landmass"); - texture = viewbox.select("#texture"); - terrs = viewbox.select("#terrs"); - biomes = viewbox.select("#biomes"); - ice = viewbox.select("#ice"); - cells = viewbox.select("#cells"); - gridOverlay = viewbox.select("#gridOverlay"); - coordinates = viewbox.select("#coordinates"); - compass = viewbox.select("#compass"); - rivers = viewbox.select("#rivers"); - terrain = viewbox.select("#terrain"); - relig = viewbox.select("#relig"); - cults = viewbox.select("#cults"); - regions = viewbox.select("#regions"); - statesBody = regions.select("#statesBody"); - statesHalo = regions.select("#statesHalo"); - provs = viewbox.select("#provs"); - zones = viewbox.select("#zones"); - borders = viewbox.select("#borders"); - stateBorders = borders.select("#stateBorders"); - provinceBorders = borders.select("#provinceBorders"); - routes = viewbox.select("#routes"); - roads = routes.select("#roads"); - trails = routes.select("#trails"); - searoutes = routes.select("#searoutes"); - temperature = viewbox.select("#temperature"); - coastline = viewbox.select("#coastline"); - prec = viewbox.select("#prec"); - population = viewbox.select("#population"); - emblems = viewbox.select("#emblems"); - labels = viewbox.select("#labels"); - icons = viewbox.select("#icons"); - burgIcons = icons.select("#burgIcons"); - anchors = icons.select("#anchors"); - armies = viewbox.select("#armies"); - markers = viewbox.select("#markers"); - ruler = viewbox.select("#ruler"); - fogging = viewbox.select("#fogging"); - debug = viewbox.select("#debug"); - burgLabels = labels.select("#burgLabels"); + svg = d3.select('#map'); + defs = svg.select('#deftemp'); + viewbox = svg.select('#viewbox'); + scaleBar = svg.select('#scaleBar'); + legend = svg.select('#legend'); + ocean = viewbox.select('#ocean'); + oceanLayers = ocean.select('#oceanLayers'); + oceanPattern = ocean.select('#oceanPattern'); + lakes = viewbox.select('#lakes'); + landmass = viewbox.select('#landmass'); + texture = viewbox.select('#texture'); + terrs = viewbox.select('#terrs'); + biomes = viewbox.select('#biomes'); + ice = viewbox.select('#ice'); + cells = viewbox.select('#cells'); + gridOverlay = viewbox.select('#gridOverlay'); + coordinates = viewbox.select('#coordinates'); + compass = viewbox.select('#compass'); + rivers = viewbox.select('#rivers'); + terrain = viewbox.select('#terrain'); + relig = viewbox.select('#relig'); + cults = viewbox.select('#cults'); + regions = viewbox.select('#regions'); + statesBody = regions.select('#statesBody'); + statesHalo = regions.select('#statesHalo'); + provs = viewbox.select('#provs'); + zones = viewbox.select('#zones'); + borders = viewbox.select('#borders'); + stateBorders = borders.select('#stateBorders'); + provinceBorders = borders.select('#provinceBorders'); + routes = viewbox.select('#routes'); + roads = routes.select('#roads'); + trails = routes.select('#trails'); + searoutes = routes.select('#searoutes'); + temperature = viewbox.select('#temperature'); + coastline = viewbox.select('#coastline'); + prec = viewbox.select('#prec'); + population = viewbox.select('#population'); + emblems = viewbox.select('#emblems'); + labels = viewbox.select('#labels'); + icons = viewbox.select('#icons'); + burgIcons = icons.select('#burgIcons'); + anchors = icons.select('#anchors'); + armies = viewbox.select('#armies'); + markers = viewbox.select('#markers'); + ruler = viewbox.select('#ruler'); + fogging = viewbox.select('#fogging'); + debug = viewbox.select('#debug'); + burgLabels = labels.select('#burgLabels'); })(); void (function parseGridData() { grid = JSON.parse(data[6]); calculateVoronoi(grid, grid.points); - grid.cells.h = Uint8Array.from(data[7].split(",")); - grid.cells.prec = Uint8Array.from(data[8].split(",")); - grid.cells.f = Uint16Array.from(data[9].split(",")); - grid.cells.t = Int8Array.from(data[10].split(",")); - grid.cells.temp = Int8Array.from(data[11].split(",")); + grid.cells.h = Uint8Array.from(data[7].split(',')); + grid.cells.prec = Uint8Array.from(data[8].split(',')); + grid.cells.f = Uint16Array.from(data[9].split(',')); + grid.cells.t = Int8Array.from(data[10].split(',')); + grid.cells.temp = Int8Array.from(data[11].split(',')); })(); void (function parsePackData() { @@ -238,110 +238,123 @@ function parseLoadedData(data) { pack.cultures = JSON.parse(data[13]); pack.states = JSON.parse(data[14]); pack.burgs = JSON.parse(data[15]); - pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: "No religion"}]; + pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: 'No religion'}]; pack.provinces = data[30] ? JSON.parse(data[30]) : [0]; pack.rivers = data[32] ? JSON.parse(data[32]) : []; + pack.resources = data[35] ? JSON.parse(data[35]) : []; const cells = pack.cells; - cells.biome = Uint8Array.from(data[16].split(",")); - cells.burg = Uint16Array.from(data[17].split(",")); - cells.conf = Uint8Array.from(data[18].split(",")); - cells.culture = Uint16Array.from(data[19].split(",")); - cells.fl = Uint16Array.from(data[20].split(",")); - cells.pop = Float32Array.from(data[21].split(",")); - cells.r = Uint16Array.from(data[22].split(",")); - cells.road = Uint16Array.from(data[23].split(",")); - cells.s = Uint16Array.from(data[24].split(",")); - cells.state = Uint16Array.from(data[25].split(",")); - cells.religion = data[26] ? Uint16Array.from(data[26].split(",")) : new Uint16Array(cells.i.length); - cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(cells.i.length); - cells.crossroad = data[28] ? Uint16Array.from(data[28].split(",")) : new Uint16Array(cells.i.length); + cells.biome = Uint8Array.from(data[16].split(',')); + cells.burg = Uint16Array.from(data[17].split(',')); + cells.conf = Uint8Array.from(data[18].split(',')); + cells.culture = Uint16Array.from(data[19].split(',')); + cells.fl = Uint16Array.from(data[20].split(',')); + cells.pop = Float32Array.from(data[21].split(',')); + cells.r = Uint16Array.from(data[22].split(',')); + cells.road = Uint16Array.from(data[23].split(',')); + cells.s = Uint16Array.from(data[24].split(',')); + cells.state = Uint16Array.from(data[25].split(',')); + cells.religion = data[26] ? Uint16Array.from(data[26].split(',')) : new Uint16Array(cells.i.length); + cells.province = data[27] ? Uint16Array.from(data[27].split(',')) : new Uint16Array(cells.i.length); + cells.crossroad = data[28] ? Uint16Array.from(data[28].split(',')) : new Uint16Array(cells.i.length); + cells.resource = data[34] ? Uint8Array.from(data[34].split(',')) : new Uint8Array(cells.i.length); if (data[31]) { - const namesDL = data[31].split("/"); + const namesDL = data[31].split('/'); namesDL.forEach((d, i) => { - const e = d.split("|"); + const e = d.split('|'); if (!e.length) return; - const b = e[5].split(",").length > 2 || !nameBases[i] ? e[5] : nameBases[i].b; + const b = e[5].split(',').length > 2 || !nameBases[i] ? e[5] : nameBases[i].b; nameBases[i] = {name: e[0], min: e[1], max: e[2], d: e[3], m: e[4], b}; }); } })(); - const notHidden = selection => selection.node() && selection.style("display") !== "none"; - const hasChildren = selection => selection.node()?.hasChildNodes(); + const notHidden = (selection) => selection.node() && selection.style('display') !== 'none'; + const hasChildren = (selection) => selection.node()?.hasChildNodes(); const hasChild = (selection, selector) => selection.node()?.querySelector(selector); - const turnOn = el => document.getElementById(el).classList.remove("buttonoff"); + const turnOn = (el) => document.getElementById(el).classList.remove('buttonoff'); void (function restoreLayersState() { // turn all layers off document - .getElementById("mapLayers") - .querySelectorAll("li") - .forEach(el => el.classList.add("buttonoff")); + .getElementById('mapLayers') + .querySelectorAll('li') + .forEach((el) => el.classList.add('buttonoff')); // turn on active layers - if (notHidden(texture) && hasChild(texture, "image")) turnOn("toggleTexture"); - if (hasChildren(terrs)) turnOn("toggleHeight"); - if (hasChildren(biomes)) turnOn("toggleBiomes"); - if (hasChildren(cells)) turnOn("toggleCells"); - if (hasChildren(gridOverlay)) turnOn("toggleGrid"); - if (hasChildren(coordinates)) turnOn("toggleCoordinates"); - if (notHidden(compass) && hasChild(compass, "use")) turnOn("toggleCompass"); - if (notHidden(rivers)) turnOn("toggleRivers"); - if (notHidden(terrain) && hasChildren(terrain)) turnOn("toggleRelief"); - if (hasChildren(relig)) turnOn("toggleReligions"); - if (hasChildren(cults)) turnOn("toggleCultures"); - if (hasChildren(statesBody)) turnOn("toggleStates"); - if (hasChildren(provs)) turnOn("toggleProvinces"); - if (hasChildren(zones) && notHidden(zones)) turnOn("toggleZones"); - if (notHidden(borders) && hasChild(compass, "use")) turnOn("toggleBorders"); - if (notHidden(routes) && hasChild(routes, "path")) turnOn("toggleRoutes"); - if (hasChildren(temperature)) turnOn("toggleTemp"); - if (hasChild(population, "line")) turnOn("togglePopulation"); - if (hasChildren(ice)) turnOn("toggleIce"); - if (hasChild(prec, "circle")) turnOn("togglePrec"); - if (notHidden(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems"); - if (notHidden(labels)) turnOn("toggleLabels"); - if (notHidden(icons)) turnOn("toggleIcons"); - if (hasChildren(armies) && notHidden(armies)) turnOn("toggleMilitary"); - if (hasChildren(markers) && notHidden(markers)) turnOn("toggleMarkers"); - if (notHidden(ruler)) turnOn("toggleRulers"); - if (notHidden(scaleBar)) turnOn("toggleScaleBar"); + if (notHidden(texture) && hasChild(texture, 'image')) turnOn('toggleTexture'); + if (hasChildren(terrs)) turnOn('toggleHeight'); + if (hasChildren(biomes)) turnOn('toggleBiomes'); + if (hasChildren(cells)) turnOn('toggleCells'); + if (hasChildren(gridOverlay)) turnOn('toggleGrid'); + if (hasChildren(coordinates)) turnOn('toggleCoordinates'); + if (notHidden(compass) && hasChild(compass, 'use')) turnOn('toggleCompass'); + if (notHidden(rivers)) turnOn('toggleRivers'); + if (notHidden(terrain) && hasChildren(terrain)) turnOn('toggleRelief'); + if (hasChildren(relig)) turnOn('toggleReligions'); + if (hasChildren(cults)) turnOn('toggleCultures'); + if (hasChildren(statesBody)) turnOn('toggleStates'); + if (hasChildren(provs)) turnOn('toggleProvinces'); + if (hasChildren(zones) && notHidden(zones)) turnOn('toggleZones'); + if (notHidden(borders) && hasChild(compass, 'use')) turnOn('toggleBorders'); + if (notHidden(routes) && hasChild(routes, 'path')) turnOn('toggleRoutes'); + if (hasChildren(temperature)) turnOn('toggleTemp'); + if (hasChild(population, 'line')) turnOn('togglePopulation'); + if (hasChildren(ice)) turnOn('toggleIce'); + if (hasChild(prec, 'circle')) turnOn('togglePrec'); + if (hasChildren(goods)) turnOn('toggleResources'); + if (notHidden(emblems) && hasChild(emblems, 'use')) turnOn('toggleEmblems'); + if (notHidden(labels)) turnOn('toggleLabels'); + if (notHidden(icons)) turnOn('toggleIcons'); + if (hasChildren(armies) && notHidden(armies)) turnOn('toggleMilitary'); + if (hasChildren(markers) && notHidden(markers)) turnOn('toggleMarkers'); + if (notHidden(ruler)) turnOn('toggleRulers'); + if (notHidden(scaleBar)) turnOn('toggleScaleBar'); getCurrentPreset(); })(); void (function restoreEvents() { - scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => editUnits()); - legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend()); + scaleBar.on('mousemove', () => tip('Click to open Units Editor')).on('click', () => editUnits()); + legend.on('mousemove', () => tip('Drag to change the position. Click to hide the legend')).on('click', () => clearLegend()); })(); void (function resolveVersionConflicts() { - const version = parseFloat(data[0].split("|")[0]); + const version = parseFloat(data[0].split('|')[0]); if (version < 0.9) { // 0.9 has additional relief icons to be included into older maps - document.getElementById("defs-relief").innerHTML = reliefIcons; + document.getElementById('defs-relief').innerHTML = reliefIcons; } if (version < 1) { // 1.0 adds a new religions layer - relig = viewbox.insert("g", "#terrain").attr("id", "relig"); + relig = viewbox.insert('g', '#terrain').attr('id', 'relig'); Religions.generate(); // 1.0 adds a legend box - legend = svg.append("g").attr("id", "legend"); - legend.attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 13).attr("data-size", 13).attr("data-x", 99).attr("data-y", 93).attr("stroke-width", 2.5).attr("stroke", "#812929").attr("stroke-dasharray", "0 4 10 4").attr("stroke-linecap", "round"); + legend = svg.append('g').attr('id', 'legend'); + legend + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', 13) + .attr('data-size', 13) + .attr('data-x', 99) + .attr('data-y', 93) + .attr('stroke-width', 2.5) + .attr('stroke', '#812929') + .attr('stroke-dasharray', '0 4 10 4') + .attr('stroke-linecap', 'round'); // 1.0 separated drawBorders fron drawStates() - stateBorders = borders.append("g").attr("id", "stateBorders"); - provinceBorders = borders.append("g").attr("id", "provinceBorders"); - borders.attr("opacity", null).attr("stroke", null).attr("stroke-width", null).attr("stroke-dasharray", null).attr("stroke-linecap", null).attr("filter", null); - stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt"); - provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt"); + stateBorders = borders.append('g').attr('id', 'stateBorders'); + provinceBorders = borders.append('g').attr('id', 'provinceBorders'); + borders.attr('opacity', null).attr('stroke', null).attr('stroke-width', null).attr('stroke-dasharray', null).attr('stroke-linecap', null).attr('filter', null); + stateBorders.attr('opacity', 0.8).attr('stroke', '#56566d').attr('stroke-width', 1).attr('stroke-dasharray', '2').attr('stroke-linecap', 'butt'); + provinceBorders.attr('opacity', 0.8).attr('stroke', '#56566d').attr('stroke-width', 0.5).attr('stroke-dasharray', '1').attr('stroke-linecap', 'butt'); // 1.0 adds state relations, provinces, forms and full names - provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6); + provs = viewbox.insert('g', '#borders').attr('id', 'provs').attr('opacity', 0.6); BurgsAndStates.collectStatistics(); BurgsAndStates.generateCampaigns(); BurgsAndStates.generateDiplomacy(); @@ -349,113 +362,113 @@ function parseLoadedData(data) { drawStates(); BurgsAndStates.generateProvinces(); drawBorders(); - if (!layerIsOn("toggleBorders")) $("#borders").fadeOut(); - if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove(); + if (!layerIsOn('toggleBorders')) $('#borders').fadeOut(); + if (!layerIsOn('toggleStates')) regions.attr('display', 'none').selectAll('path').remove(); // 1.0 adds hatching - document.getElementsByTagName("defs")[0].appendChild(hatching); + document.getElementsByTagName('defs')[0].appendChild(hatching); // 1.0 adds zones layer - zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none"); - zones.attr("opacity", 0.6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt"); + zones = viewbox.insert('g', '#borders').attr('id', 'zones').attr('display', 'none'); + zones.attr('opacity', 0.6).attr('stroke', null).attr('stroke-width', 0).attr('stroke-dasharray', null).attr('stroke-linecap', 'butt'); addZones(); - if (!markers.selectAll("*").size()) { + if (!markers.selectAll('*').size()) { addMarkers(); - turnButtonOn("toggleMarkers"); + turnButtonOn('toggleMarkers'); } // 1.0 add fogging layer (state focus) - fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)").append("g").attr("id", "fogging").style("display", "none"); - fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%"); - defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("fill", "white"); + fogging = viewbox.insert('g', '#ruler').attr('id', 'fogging-cont').attr('mask', 'url(#fog)').append('g').attr('id', 'fogging').style('display', 'none'); + fogging.append('rect').attr('x', 0).attr('y', 0).attr('width', '100%').attr('height', '100%'); + defs.append('mask').attr('id', 'fog').append('rect').attr('x', 0).attr('y', 0).attr('width', '100%').attr('height', '100%').attr('fill', 'white'); // 1.0 changes states opacity bask to regions level - if (statesBody.attr("opacity")) { - regions.attr("opacity", statesBody.attr("opacity")); - statesBody.attr("opacity", null); + if (statesBody.attr('opacity')) { + regions.attr('opacity', statesBody.attr('opacity')); + statesBody.attr('opacity', null); } // 1.0 changed labels to multi-lined - labels.selectAll("textPath").each(function () { + labels.selectAll('textPath').each(function () { const text = this.textContent; const shift = this.getComputedTextLength() / -1.5; this.innerHTML = `${text}`; }); // 1.0 added new biome - Wetland - biomesData.name.push("Wetland"); - biomesData.color.push("#0b9131"); + biomesData.name.push('Wetland'); + biomesData.color.push('#0b9131'); biomesData.habitability.push(12); } if (version < 1.1) { // v 1.0 initial code had a bug with religion layer id - if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig"); + if (!relig.size()) relig = viewbox.insert('g', '#terrain').attr('id', 'relig'); // v 1.0 initially has Sympathy status then relaced with Friendly for (const s of pack.states) { if (!s.diplomacy) continue; - s.diplomacy = s.diplomacy.map(r => (r === "Sympathy" ? "Friendly" : r)); + s.diplomacy = s.diplomacy.map((r) => (r === 'Sympathy' ? 'Friendly' : r)); } // labels should be toggled via style attribute, so remove display attribute - labels.attr("display", null); + labels.attr('display', null); // v 1.0 added religions heirarchy tree if (pack.religions[1] && !pack.religions[1].code) { pack.religions - .filter(r => r.i) - .forEach(r => { + .filter((r) => r.i) + .forEach((r) => { r.origin = 0; r.code = r.name.slice(0, 2); }); } - if (!document.getElementById("freshwater")) { - lakes.append("g").attr("id", "freshwater"); - lakes.select("#freshwater").attr("opacity", 0.5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", 0.7).attr("filter", null); + if (!document.getElementById('freshwater')) { + lakes.append('g').attr('id', 'freshwater'); + lakes.select('#freshwater').attr('opacity', 0.5).attr('fill', '#a6c1fd').attr('stroke', '#5f799d').attr('stroke-width', 0.7).attr('filter', null); } - if (!document.getElementById("salt")) { - lakes.append("g").attr("id", "salt"); - lakes.select("#salt").attr("opacity", 0.5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", 0.7).attr("filter", null); + if (!document.getElementById('salt')) { + lakes.append('g').attr('id', 'salt'); + lakes.select('#salt').attr('opacity', 0.5).attr('fill', '#409b8a').attr('stroke', '#388985').attr('stroke-width', 0.7).attr('filter', null); } // v 1.1 added new lake and coast groups - if (!document.getElementById("sinkhole")) { - lakes.append("g").attr("id", "sinkhole"); - lakes.append("g").attr("id", "frozen"); - lakes.append("g").attr("id", "lava"); - lakes.select("#sinkhole").attr("opacity", 1).attr("fill", "#5bc9fd").attr("stroke", "#53a3b0").attr("stroke-width", 0.7).attr("filter", null); - lakes.select("#frozen").attr("opacity", 0.95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null); - lakes.select("#lava").attr("opacity", 0.7).attr("fill", "#90270d").attr("stroke", "#f93e0c").attr("stroke-width", 2).attr("filter", "url(#crumpled)"); + if (!document.getElementById('sinkhole')) { + lakes.append('g').attr('id', 'sinkhole'); + lakes.append('g').attr('id', 'frozen'); + lakes.append('g').attr('id', 'lava'); + lakes.select('#sinkhole').attr('opacity', 1).attr('fill', '#5bc9fd').attr('stroke', '#53a3b0').attr('stroke-width', 0.7).attr('filter', null); + lakes.select('#frozen').attr('opacity', 0.95).attr('fill', '#cdd4e7').attr('stroke', '#cfe0eb').attr('stroke-width', 0).attr('filter', null); + lakes.select('#lava').attr('opacity', 0.7).attr('fill', '#90270d').attr('stroke', '#f93e0c').attr('stroke-width', 2).attr('filter', 'url(#crumpled)'); - coastline.append("g").attr("id", "sea_island"); - coastline.append("g").attr("id", "lake_island"); - coastline.select("#sea_island").attr("opacity", 0.5).attr("stroke", "#1f3846").attr("stroke-width", 0.7).attr("filter", "url(#dropShadow)"); - coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", 0.35).attr("filter", null); + coastline.append('g').attr('id', 'sea_island'); + coastline.append('g').attr('id', 'lake_island'); + coastline.select('#sea_island').attr('opacity', 0.5).attr('stroke', '#1f3846').attr('stroke-width', 0.7).attr('filter', 'url(#dropShadow)'); + coastline.select('#lake_island').attr('opacity', 1).attr('stroke', '#7c8eaf').attr('stroke-width', 0.35).attr('filter', null); } // v 1.1 features stores more data - defs.select("#land").selectAll("path").remove(); - defs.select("#water").selectAll("path").remove(); - coastline.selectAll("path").remove(); - lakes.selectAll("path").remove(); + defs.select('#land').selectAll('path').remove(); + defs.select('#water').selectAll('path').remove(); + coastline.selectAll('path').remove(); + lakes.selectAll('path').remove(); drawCoastline(); } if (version < 1.11) { // v 1.11 added new attributes - terrs.attr("scheme", "bright").attr("terracing", 0).attr("skip", 5).attr("relax", 0).attr("curve", 0); - svg.select("#oceanic > *").attr("id", "oceanicPattern"); - oceanLayers.attr("layers", "-6,-3,-1"); - gridOverlay.attr("type", "pointyHex").attr("size", 10); + terrs.attr('scheme', 'bright').attr('terracing', 0).attr('skip', 5).attr('relax', 0).attr('curve', 0); + svg.select('#oceanic > *').attr('id', 'oceanicPattern'); + oceanLayers.attr('layers', '-6,-3,-1'); + gridOverlay.attr('type', 'pointyHex').attr('size', 10); // v 1.11 added cultures heirarchy tree if (pack.cultures[1] && !pack.cultures[1].code) { pack.cultures - .filter(c => c.i) - .forEach(c => { + .filter((c) => c.i) + .forEach((c) => { c.origin = 0; c.code = c.name.slice(0, 2); }); @@ -465,23 +478,23 @@ function parseLoadedData(data) { unfog(); // v 1.2 added new terrain attributes - if (!terrain.attr("set")) terrain.attr("set", "simple"); - if (!terrain.attr("size")) terrain.attr("size", 1); - if (!terrain.attr("density")) terrain.attr("density", 0.4); + if (!terrain.attr('set')) terrain.attr('set', 'simple'); + if (!terrain.attr('size')) terrain.attr('size', 1); + if (!terrain.attr('density')) terrain.attr('density', 0.4); } if (version < 1.21) { // v 1.11 replaced "display" attribute by "display" style - viewbox.selectAll("g").each(function () { - if (this.hasAttribute("display")) { - this.removeAttribute("display"); - this.style.display = "none"; + viewbox.selectAll('g').each(function () { + if (this.hasAttribute('display')) { + this.removeAttribute('display'); + this.style.display = 'none'; } }); // v 1.21 added rivers data to pack pack.rivers = []; // rivers data - rivers.selectAll("path").each(function () { + rivers.selectAll('path').each(function () { const i = +this.id.slice(5); const length = this.getTotalLength() / 2; const s = this.getPointAtLength(length), @@ -489,7 +502,7 @@ function parseLoadedData(data) { const source = findCell(s.x, s.y), mouth = findCell(e.x, e.y); const name = Rivers.getName(mouth); - const type = length < 25 ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : "River"; + const type = length < 25 ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : 'River'; pack.rivers.push({i, parent: 0, length, source, mouth, basin: i, name, type}); }); } @@ -503,8 +516,8 @@ function parseLoadedData(data) { // v 1.3 added global options object const winds = options.slice(); // previostly wind was saved in settings[19] const year = rand(100, 2000); - const era = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + " Era"; - const eraShort = era[0] + "E"; + const era = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + ' Era'; + const eraShort = era[0] + 'E'; const military = Military.getDefaultOptions(); options = {winds, year, era, eraShort, military}; @@ -512,22 +525,22 @@ function parseLoadedData(data) { BurgsAndStates.generateCampaigns(); // v 1.3 added militry layer - armies = viewbox.insert("g", "#icons").attr("id", "armies"); - armies.attr("opacity", 1).attr("fill-opacity", 1).attr("font-size", 6).attr("box-size", 3).attr("stroke", "#000").attr("stroke-width", 0.3); - turnButtonOn("toggleMilitary"); + armies = viewbox.insert('g', '#icons').attr('id', 'armies'); + armies.attr('opacity', 1).attr('fill-opacity', 1).attr('font-size', 6).attr('box-size', 3).attr('stroke', '#000').attr('stroke-width', 0.3); + turnButtonOn('toggleMilitary'); Military.generate(); } if (version < 1.4) { // v 1.35 added dry lakes - if (!lakes.select("#dry").size()) { - lakes.append("g").attr("id", "dry"); - lakes.select("#dry").attr("opacity", 1).attr("fill", "#c9bfa7").attr("stroke", "#8e816f").attr("stroke-width", 0.7).attr("filter", null); + if (!lakes.select('#dry').size()) { + lakes.append('g').attr('id', 'dry'); + lakes.select('#dry').attr('opacity', 1).attr('fill', '#c9bfa7').attr('stroke', '#8e816f').attr('stroke-width', 0.7).attr('filter', null); } // v 1.4 added ice layer - ice = viewbox.insert("g", "#coastline").attr("id", "ice").style("display", "none"); - ice.attr("opacity", null).attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("stroke-width", 1).attr("filter", "url(#dropShadow05)"); + ice = viewbox.insert('g', '#coastline').attr('id', 'ice').style('display', 'none'); + ice.attr('opacity', null).attr('fill', '#e8f0f6').attr('stroke', '#e8f0f6').attr('stroke-width', 1).attr('filter', 'url(#dropShadow05)'); drawIce(); // v 1.4 added icon and power attributes for units @@ -537,66 +550,66 @@ function parseLoadedData(data) { } function getUnitIcon(type) { - if (type === "naval") return "🌊"; - if (type === "ranged") return "🏹"; - if (type === "mounted") return "🐴"; - if (type === "machinery") return "💣"; - if (type === "armored") return "🐢"; - if (type === "aviation") return "🦅"; - if (type === "magical") return "🔮"; - else return "⚔️"; + if (type === 'naval') return '🌊'; + if (type === 'ranged') return '🏹'; + if (type === 'mounted') return '🐴'; + if (type === 'machinery') return '💣'; + if (type === 'armored') return '🐢'; + if (type === 'aviation') return '🦅'; + if (type === 'magical') return '🔮'; + else return '⚔️'; } // 1.4 added state reference for regiments - pack.states.filter(s => s.military).forEach(s => s.military.forEach(r => (r.state = s.i))); + pack.states.filter((s) => s.military).forEach((s) => s.military.forEach((r) => (r.state = s.i))); } if (version < 1.5) { // not need to store default styles from v 1.5 - localStorage.removeItem("styleClean"); - localStorage.removeItem("styleGloom"); - localStorage.removeItem("styleAncient"); - localStorage.removeItem("styleMonochrome"); + localStorage.removeItem('styleClean'); + localStorage.removeItem('styleGloom'); + localStorage.removeItem('styleAncient'); + localStorage.removeItem('styleMonochrome'); // v 1.5 cultures has shield attribute - pack.cultures.forEach(culture => { + pack.cultures.forEach((culture) => { if (culture.removed) return; culture.shield = Cultures.getRandomShield(); }); // v 1.5 added burg type value - pack.burgs.forEach(burg => { + pack.burgs.forEach((burg) => { if (!burg.i || burg.removed) return; burg.type = BurgsAndStates.getType(burg.cell, burg.port); }); // v 1.5 added emblems - defs.append("g").attr("id", "defs-emblems"); - emblems = viewbox.insert("g", "#population").attr("id", "emblems").style("display", "none"); - emblems.append("g").attr("id", "burgEmblems"); - emblems.append("g").attr("id", "provinceEmblems"); - emblems.append("g").attr("id", "stateEmblems"); + defs.append('g').attr('id', 'defs-emblems'); + emblems = viewbox.insert('g', '#population').attr('id', 'emblems').style('display', 'none'); + emblems.append('g').attr('id', 'burgEmblems'); + emblems.append('g').attr('id', 'provinceEmblems'); + emblems.append('g').attr('id', 'stateEmblems'); regenerateEmblems(); toggleEmblems(); // v 1.5 changed releif icons data - terrain.selectAll("use").each(function () { - const type = this.getAttribute("data-type") || this.getAttribute("xlink:href"); - this.removeAttribute("xlink:href"); - this.removeAttribute("data-type"); - this.removeAttribute("data-size"); - this.setAttribute("href", type); + terrain.selectAll('use').each(function () { + const type = this.getAttribute('data-type') || this.getAttribute('xlink:href'); + this.removeAttribute('xlink:href'); + this.removeAttribute('data-type'); + this.removeAttribute('data-size'); + this.setAttribute('href', type); }); } if (version < 1.6) { // v 1.6 changed rivers data for (const river of pack.rivers) { - const el = document.getElementById("river" + river.i); + const el = document.getElementById('river' + river.i); if (el) { - river.widthFactor = +el.getAttribute("data-width"); - el.removeAttribute("data-width"); - el.removeAttribute("data-increment"); + river.widthFactor = +el.getAttribute('data-width'); + el.removeAttribute('data-width'); + el.removeAttribute('data-increment'); river.discharge = pack.cells.fl[river.mouth] || 1; river.width = rn(river.length / 100, 2); river.sourceWidth = 0.1; @@ -607,12 +620,12 @@ function parseLoadedData(data) { // v 1.6 changed lakes data for (const f of pack.features) { - if (f.type !== "lake") continue; + if (f.type !== 'lake') continue; if (f.evaporation) continue; f.flux = f.flux || f.cells * 3; f.temp = grid.cells.temp[pack.cells.g[f.firstCell]]; - f.height = f.height || d3.min(pack.cells.c[f.firstCell].map(c => pack.cells.h[c]).filter(h => h >= 20)); + f.height = f.height || d3.min(pack.cells.c[f.firstCell].map((c) => pack.cells.h[c]).filter((h) => h >= 20)); const height = (f.height - 18) ** heightExponentInput.value; const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp); f.evaporation = rn(evaporation * f.cells); @@ -623,14 +636,14 @@ function parseLoadedData(data) { if (version < 1.61) { // v 1.61 changed rulers data - ruler.style("display", null); + ruler.style('display', null); rulers = new Rulers(); - ruler.selectAll(".ruler > .white").each(function () { - const x1 = +this.getAttribute("x1"); - const y1 = +this.getAttribute("y1"); - const x2 = +this.getAttribute("x2"); - const y2 = +this.getAttribute("y2"); + ruler.selectAll('.ruler > .white').each(function () { + const x1 = +this.getAttribute('x1'); + const y1 = +this.getAttribute('y1'); + const x2 = +this.getAttribute('x2'); + const y2 = +this.getAttribute('y2'); if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) return; const points = [ [x1, y1], @@ -639,14 +652,14 @@ function parseLoadedData(data) { rulers.create(Ruler, points); }); - ruler.selectAll("g.opisometer").each(function () { + ruler.selectAll('g.opisometer').each(function () { const pointsString = this.dataset.points; if (!pointsString) return; const points = JSON.parse(pointsString); rulers.create(Opisometer, points); }); - ruler.selectAll("path.planimeter").each(function () { + ruler.selectAll('path.planimeter').each(function () { const length = this.getTotalLength(); if (length < 30) return; @@ -661,37 +674,44 @@ function parseLoadedData(data) { rulers.create(Planimeter, points); }); - ruler.selectAll("*").remove(); + ruler.selectAll('*').remove(); if (rulers.data.length) { - turnButtonOn("toggleRulers"); + turnButtonOn('toggleRulers'); rulers.draw(); - } else turnButtonOff("toggleRulers"); + } else turnButtonOff('toggleRulers'); // 1.61 changed oceanicPattern from rect to image - const pattern = document.getElementById("oceanic"); - const filter = pattern.firstElementChild.getAttribute("filter"); - const href = filter ? "./images/" + filter.replace("url(#", "").replace(")", "") + ".png" : ""; + const pattern = document.getElementById('oceanic'); + const filter = pattern.firstElementChild.getAttribute('filter'); + const href = filter ? './images/' + filter.replace('url(#', '').replace(')', '') + '.png' : ''; pattern.innerHTML = ``; } if (version < 1.62) { // v 1.62 changed grid data - gridOverlay.attr("size", null); + gridOverlay.attr('size', null); } if (version < 1.63) { // v.1.63 change ocean pattern opacity element - const oceanPattern = document.getElementById("oceanPattern"); - if (oceanPattern) oceanPattern.removeAttribute("opacity"); - const oceanicPattern = document.getElementById("oceanicPattern"); - if (!oceanicPattern.getAttribute("opacity")) oceanicPattern.setAttribute("opacity", 0.2); + const oceanPattern = document.getElementById('oceanPattern'); + if (oceanPattern) oceanPattern.removeAttribute('opacity'); + const oceanicPattern = document.getElementById('oceanicPattern'); + if (!oceanicPattern.getAttribute('opacity')) oceanicPattern.setAttribute('opacity', 0.2); // v 1.63 moved label text-shadow from css to editable inline style - burgLabels.select("#cities").style("text-shadow", "white 0 0 4px"); - burgLabels.select("#towns").style("text-shadow", "white 0 0 4px"); - labels.select("#states").style("text-shadow", "white 0 0 4px"); - labels.select("#addedLabels").style("text-shadow", "white 0 0 4px"); + burgLabels.select('#cities').style('text-shadow', 'white 0 0 4px'); + burgLabels.select('#towns').style('text-shadow', 'white 0 0 4px'); + labels.select('#states').style('text-shadow', 'white 0 0 4px'); + labels.select('#addedLabels').style('text-shadow', 'white 0 0 4px'); + } + + if (version < 1.7) { + // v 1.7 added resources layer + goods = viewbox.append('g').attr('id', 'goods'); + defs.append('g').attr('id', 'defs-icons'); + Resources.generate(); } })(); @@ -699,83 +719,83 @@ function parseLoadedData(data) { const cells = pack.cells; if (pack.cells.i.length !== pack.cells.state.length) { - ERROR && console.error("Striping issue. Map data is corrupted. The only solution is to edit the heightmap in erase mode"); + ERROR && console.error('Striping issue. Map data is corrupted. The only solution is to edit the heightmap in erase mode'); } - const invalidStates = [...new Set(cells.state)].filter(s => !pack.states[s] || pack.states[s].removed); - invalidStates.forEach(s => { - const invalidCells = cells.i.filter(i => cells.state[i] === s); - invalidCells.forEach(i => (cells.state[i] = 0)); - ERROR && console.error("Data Integrity Check. Invalid state", s, "is assigned to cells", invalidCells); + const invalidStates = [...new Set(cells.state)].filter((s) => !pack.states[s] || pack.states[s].removed); + invalidStates.forEach((s) => { + const invalidCells = cells.i.filter((i) => cells.state[i] === s); + invalidCells.forEach((i) => (cells.state[i] = 0)); + ERROR && console.error('Data Integrity Check. Invalid state', s, 'is assigned to cells', invalidCells); }); - const invalidProvinces = [...new Set(cells.province)].filter(p => p && (!pack.provinces[p] || pack.provinces[p].removed)); - invalidProvinces.forEach(p => { - const invalidCells = cells.i.filter(i => cells.province[i] === p); - invalidCells.forEach(i => (cells.province[i] = 0)); - ERROR && console.error("Data Integrity Check. Invalid province", p, "is assigned to cells", invalidCells); + const invalidProvinces = [...new Set(cells.province)].filter((p) => p && (!pack.provinces[p] || pack.provinces[p].removed)); + invalidProvinces.forEach((p) => { + const invalidCells = cells.i.filter((i) => cells.province[i] === p); + invalidCells.forEach((i) => (cells.province[i] = 0)); + ERROR && console.error('Data Integrity Check. Invalid province', p, 'is assigned to cells', invalidCells); }); - const invalidCultures = [...new Set(cells.culture)].filter(c => !pack.cultures[c] || pack.cultures[c].removed); - invalidCultures.forEach(c => { - const invalidCells = cells.i.filter(i => cells.culture[i] === c); - invalidCells.forEach(i => (cells.province[i] = 0)); - ERROR && console.error("Data Integrity Check. Invalid culture", c, "is assigned to cells", invalidCells); + const invalidCultures = [...new Set(cells.culture)].filter((c) => !pack.cultures[c] || pack.cultures[c].removed); + invalidCultures.forEach((c) => { + const invalidCells = cells.i.filter((i) => cells.culture[i] === c); + invalidCells.forEach((i) => (cells.province[i] = 0)); + ERROR && console.error('Data Integrity Check. Invalid culture', c, 'is assigned to cells', invalidCells); }); - const invalidReligions = [...new Set(cells.religion)].filter(r => !pack.religions[r] || pack.religions[r].removed); - invalidReligions.forEach(r => { - const invalidCells = cells.i.filter(i => cells.religion[i] === r); - invalidCells.forEach(i => (cells.religion[i] = 0)); - ERROR && console.error("Data Integrity Check. Invalid religion", c, "is assigned to cells", invalidCells); + const invalidReligions = [...new Set(cells.religion)].filter((r) => !pack.religions[r] || pack.religions[r].removed); + invalidReligions.forEach((r) => { + const invalidCells = cells.i.filter((i) => cells.religion[i] === r); + invalidCells.forEach((i) => (cells.religion[i] = 0)); + ERROR && console.error('Data Integrity Check. Invalid religion', c, 'is assigned to cells', invalidCells); }); - const invalidFeatures = [...new Set(cells.f)].filter(f => f && !pack.features[f]); - invalidFeatures.forEach(f => { - const invalidCells = cells.i.filter(i => cells.f[i] === f); + const invalidFeatures = [...new Set(cells.f)].filter((f) => f && !pack.features[f]); + invalidFeatures.forEach((f) => { + const invalidCells = cells.i.filter((i) => cells.f[i] === f); // No fix as for now - ERROR && console.error("Data Integrity Check. Invalid feature", f, "is assigned to cells", invalidCells); + ERROR && console.error('Data Integrity Check. Invalid feature', f, 'is assigned to cells', invalidCells); }); - const invalidBurgs = [...new Set(cells.burg)].filter(b => b && (!pack.burgs[b] || pack.burgs[b].removed)); - invalidBurgs.forEach(b => { - const invalidCells = cells.i.filter(i => cells.burg[i] === b); - invalidCells.forEach(i => (cells.burg[i] = 0)); - ERROR && console.error("Data Integrity Check. Invalid burg", b, "is assigned to cells", invalidCells); + const invalidBurgs = [...new Set(cells.burg)].filter((b) => b && (!pack.burgs[b] || pack.burgs[b].removed)); + invalidBurgs.forEach((b) => { + const invalidCells = cells.i.filter((i) => cells.burg[i] === b); + invalidCells.forEach((i) => (cells.burg[i] = 0)); + ERROR && console.error('Data Integrity Check. Invalid burg', b, 'is assigned to cells', invalidCells); }); - const invalidRivers = [...new Set(cells.r)].filter(r => r && !pack.rivers.find(river => river.i === r)); - invalidRivers.forEach(r => { - const invalidCells = cells.i.filter(i => cells.r[i] === r); - invalidCells.forEach(i => (cells.r[i] = 0)); - rivers.select("river" + r).remove(); - ERROR && console.error("Data Integrity Check. Invalid river", r, "is assigned to cells", invalidCells); + const invalidRivers = [...new Set(cells.r)].filter((r) => r && !pack.rivers.find((river) => river.i === r)); + invalidRivers.forEach((r) => { + const invalidCells = cells.i.filter((i) => cells.r[i] === r); + invalidCells.forEach((i) => (cells.r[i] = 0)); + rivers.select('river' + r).remove(); + ERROR && console.error('Data Integrity Check. Invalid river', r, 'is assigned to cells', invalidCells); }); - pack.burgs.forEach(b => { + pack.burgs.forEach((b) => { if (!b.i || b.removed) return; if (b.port < 0) { - ERROR && console.error("Data Integrity Check. Burg", b.i, "has invalid port value", b.port); + ERROR && console.error('Data Integrity Check. Burg', b.i, 'has invalid port value', b.port); b.port = 0; } if (b.cell >= cells.i.length) { - ERROR && console.error("Data Integrity Check. Burg", b.i, "is linked to invalid cell", b.cell); + ERROR && console.error('Data Integrity Check. Burg', b.i, 'is linked to invalid cell', b.cell); b.cell = findCell(b.x, b.y); - cells.i.filter(i => cells.burg[i] === b.i).forEach(i => (cells.burg[i] = 0)); + cells.i.filter((i) => cells.burg[i] === b.i).forEach((i) => (cells.burg[i] = 0)); cells.burg[b.cell] = b.i; } if (b.state && !pack.states[b.state]) { - ERROR && console.error("Data Integrity Check. Burg", b.i, "is linked to invalid state", b.state); + ERROR && console.error('Data Integrity Check. Burg', b.i, 'is linked to invalid state', b.state); b.state = 0; } }); - pack.provinces.forEach(p => { + pack.provinces.forEach((p) => { if (!p.i || p.removed) return; if (pack.states[p.state] && !pack.states[p.state].removed) return; - ERROR && console.error("Data Integrity Check. Province", p.i, "is linked to removed state", p.state); + ERROR && console.error('Data Integrity Check. Province', p.i, 'is linked to removed state', p.state); p.removed = true; // remove incorrect province }); })(); @@ -783,11 +803,11 @@ function parseLoadedData(data) { changeMapSize(); // remove href from emblems, to trigger rendering on load - emblems.selectAll("use").attr("href", null); + emblems.selectAll('use').attr('href', null); // draw data layers (no kept in svg) - if (rulers && layerIsOn("toggleRulers")) rulers.draw(); - if (layerIsOn("toggleGrid")) drawGrid(); + if (rulers && layerIsOn('toggleRulers')) rulers.draw(); + if (layerIsOn('toggleGrid')) drawGrid(); // set options yearInput.value = options.year; @@ -799,8 +819,8 @@ function parseLoadedData(data) { WARN && console.warn(`TOTAL: ${rn((performance.now() - uploadMap.timeStart) / 1000, 2)}s`); showStatistics(); - INFO && console.groupEnd("Loaded Map " + seed); - tip("Map is successfully loaded", true, "success", 7000); + INFO && console.groupEnd('Loaded Map ' + seed); + tip('Map is successfully loaded', true, 'success', 7000); } catch (error) { ERROR && console.error(error); clearMainTip(); @@ -808,24 +828,24 @@ function parseLoadedData(data) { alertMessage.innerHTML = `An error is occured on map loading. Select a different file to load,
generate a new random map or cancel the loading

${parseError(error)}

`; - $("#alert").dialog({ + $('#alert').dialog({ resizable: false, - title: "Loading error", - maxWidth: "50em", + title: 'Loading error', + maxWidth: '50em', buttons: { - "Select file": function () { - $(this).dialog("close"); + 'Select file': function () { + $(this).dialog('close'); mapToLoad.click(); }, - "New map": function () { - $(this).dialog("close"); + 'New map': function () { + $(this).dialog('close'); regenerateMap(); }, Cancel: function () { - $(this).dialog("close"); + $(this).dialog('close'); } }, - position: {my: "center", at: "center", of: "svg"} + position: {my: 'center', at: 'center', of: 'svg'} }); } } diff --git a/modules/save.js b/modules/save.js index 7d109e10..76f51d96 100644 --- a/modules/save.js +++ b/modules/save.js @@ -1,27 +1,27 @@ // Functions to save and load the map -"use strict"; +'use strict'; // download map as SVG async function saveSVG() { - TIME && console.time("saveSVG"); - const url = await getMapURL("svg"); - const link = document.createElement("a"); - link.download = getFileName() + ".svg"; + TIME && console.time('saveSVG'); + const url = await getMapURL('svg'); + const link = document.createElement('a'); + link.download = getFileName() + '.svg'; link.href = url; link.click(); - tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, "success", 5000); - TIME && console.timeEnd("saveSVG"); + tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, 'success', 5000); + TIME && console.timeEnd('saveSVG'); } // download map as PNG async function savePNG() { - TIME && console.time("savePNG"); - const url = await getMapURL("png"); + TIME && console.time('savePNG'); + const url = await getMapURL('png'); - const link = document.createElement("a"); - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); + const link = document.createElement('a'); + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); canvas.width = svgWidth * pngResolutionInput.value; canvas.height = svgHeight * pngResolutionInput.value; const img = new Image(); @@ -29,56 +29,56 @@ async function savePNG() { img.onload = function () { ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - link.download = getFileName() + ".png"; + link.download = getFileName() + '.png'; canvas.toBlob(function (blob) { link.href = window.URL.createObjectURL(blob); link.click(); window.setTimeout(function () { canvas.remove(); window.URL.revokeObjectURL(link.href); - tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, "success", 5000); + tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check. You can set image scale in options`, true, 'success', 5000); }, 1000); }); }; - TIME && console.timeEnd("savePNG"); + TIME && console.timeEnd('savePNG'); } // download map as JPEG async function saveJPEG() { - TIME && console.time("saveJPEG"); - const url = await getMapURL("png"); + TIME && console.time('saveJPEG'); + const url = await getMapURL('png'); - const canvas = document.createElement("canvas"); + const canvas = document.createElement('canvas'); canvas.width = svgWidth * pngResolutionInput.value; canvas.height = svgHeight * pngResolutionInput.value; const img = new Image(); img.src = url; img.onload = async function () { - canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height); + canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height); const quality = Math.min(rn(1 - pngResolutionInput.value / 20, 2), 0.92); - const URL = await canvas.toDataURL("image/jpeg", quality); - const link = document.createElement("a"); - link.download = getFileName() + ".jpeg"; + const URL = await canvas.toDataURL('image/jpeg', quality); + const link = document.createElement('a'); + link.download = getFileName() + '.jpeg'; link.href = URL; link.click(); - tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); + tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, 'success', 7000); window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000); }; - TIME && console.timeEnd("saveJPEG"); + TIME && console.timeEnd('saveJPEG'); } // download map as png tiles async function saveTiles() { return new Promise(async (resolve, reject) => { // download schema - const urlSchema = await getMapURL("tiles", "schema"); + const urlSchema = await getMapURL('tiles', 'schema'); const zip = new JSZip(); - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); canvas.width = graphWidth; canvas.height = graphHeight; @@ -86,14 +86,14 @@ async function saveTiles() { imgSchema.src = urlSchema; imgSchema.onload = function () { ctx.drawImage(imgSchema, 0, 0, canvas.width, canvas.height); - canvas.toBlob(blob => zip.file(`fmg_tile_schema.png`, blob)); + canvas.toBlob((blob) => zip.file(`fmg_tile_schema.png`, blob)); }; // download tiles - const url = await getMapURL("tiles"); - const tilesX = +document.getElementById("tileColsInput").value; - const tilesY = +document.getElementById("tileRowsInput").value; - const scale = +document.getElementById("tileScaleInput").value; + const url = await getMapURL('tiles'); + const tilesX = +document.getElementById('tileColsInput').value; + const tilesY = +document.getElementById('tileRowsInput').value; + const scale = +document.getElementById('tileScaleInput').value; const tileW = (graphWidth / tilesX) | 0; const tileH = (graphHeight / tilesY) | 0; @@ -112,7 +112,7 @@ async function saveTiles() { for (let x = 0; x + tileW <= graphWidth; x += tileW, i++) { ctx.drawImage(img, x, y, tileW, tileH, 0, 0, width, height); const name = `fmg_tile_${i}.png`; - canvas.toBlob(blob => { + canvas.toBlob((blob) => { zip.file(name, blob); loaded += 1; if (loaded === tolesTotal) return downloadZip(); @@ -123,8 +123,8 @@ async function saveTiles() { function downloadZip() { const name = `${getFileName()}.zip`; - zip.generateAsync({type: "blob"}).then(blob => { - const link = document.createElement("a"); + zip.generateAsync({type: 'blob'}).then((blob) => { + const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = name; link.click(); @@ -139,43 +139,43 @@ async function saveTiles() { // parse map svg to object url async function getMapURL(type, subtype) { - const cloneEl = document.getElementById("map").cloneNode(true); // clone svg - cloneEl.id = "fantasyMap"; + const cloneEl = document.getElementById('map').cloneNode(true); // clone svg + cloneEl.id = 'fantasyMap'; document.body.appendChild(cloneEl); const clone = d3.select(cloneEl); - if (subtype !== "schema") clone.select("#debug").remove(); + if (subtype !== 'schema') clone.select('#debug').remove(); - const cloneDefs = cloneEl.getElementsByTagName("defs")[0]; - const svgDefs = document.getElementById("defElements"); + const cloneDefs = cloneEl.getElementsByTagName('defs')[0]; + const svgDefs = document.getElementById('defElements'); - const isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1; - if (isFirefox && type === "mesh") clone.select("#oceanPattern").remove(); - if (subtype === "globe") clone.select("#scaleBar").remove(); - if (subtype === "noWater") { - clone.select("#oceanBase").attr("opacity", 0); - clone.select("#oceanPattern").attr("opacity", 0); + const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + if (isFirefox && type === 'mesh') clone.select('#oceanPattern').remove(); + if (subtype === 'globe') clone.select('#scaleBar').remove(); + if (subtype === 'noWater') { + clone.select('#oceanBase').attr('opacity', 0); + clone.select('#oceanPattern').attr('opacity', 0); } - if (type !== "png") { + if (type !== 'png') { // reset transform to show the whole map - clone.attr("width", graphWidth).attr("height", graphHeight); - clone.select("#viewbox").attr("transform", null); + clone.attr('width', graphWidth).attr('height', graphHeight); + clone.select('#viewbox').attr('transform', null); } - if (type === "svg") removeUnusedElements(clone); - if (customization && type === "mesh") updateMeshCells(clone); + if (type === 'svg') removeUnusedElements(clone); + if (customization && type === 'mesh') updateMeshCells(clone); inlineStyle(clone); // remove unused filters - const filters = cloneEl.querySelectorAll("filter"); + const filters = cloneEl.querySelectorAll('filter'); for (let i = 0; i < filters.length; i++) { const id = filters[i].id; if (cloneEl.querySelector("[filter='url(#" + id + ")']")) continue; - if (cloneEl.getAttribute("filter") === "url(#" + id + ")") continue; + if (cloneEl.getAttribute('filter') === 'url(#' + id + ')') continue; filters[i].remove(); } // remove unused patterns - const patterns = cloneEl.querySelectorAll("pattern"); + const patterns = cloneEl.querySelectorAll('pattern'); for (let i = 0; i < patterns.length; i++) { const id = patterns[i].id; if (cloneEl.querySelector("[fill='url(#" + id + ")']")) continue; @@ -183,7 +183,7 @@ async function getMapURL(type, subtype) { } // remove unused symbols - const symbols = cloneEl.querySelectorAll("symbol"); + const symbols = cloneEl.querySelectorAll('symbol'); for (let i = 0; i < symbols.length; i++) { const id = symbols[i].id; if (cloneEl.querySelector("use[*|href='#" + id + "']")) continue; @@ -191,42 +191,44 @@ async function getMapURL(type, subtype) { } // add displayed emblems - if (layerIsOn("toggleEmblems") && emblems.selectAll("use").size()) { + if (layerIsOn('toggleEmblems') && emblems.selectAll('use').size()) { cloneEl - .getElementById("emblems") - ?.querySelectorAll("use") - .forEach(el => { - const href = el.getAttribute("href") || el.getAttribute("xlink:href"); + .getElementById('emblems') + ?.querySelectorAll('use') + .forEach((el) => { + const href = el.getAttribute('href') || el.getAttribute('xlink:href'); if (!href) return; const emblem = document.getElementById(href.slice(1)); if (emblem) cloneDefs.append(emblem.cloneNode(true)); }); } else { - cloneDefs.querySelector("#defs-emblems")?.remove(); + cloneDefs.querySelector('#defs-emblems')?.remove(); } + // add resources TODO + // replace ocean pattern href to base64 - if (PRODUCTION && cloneEl.getElementById("oceanicPattern")) { - const el = cloneEl.getElementById("oceanicPattern"); - const url = el.getAttribute("href"); - await new Promise(resolve => { - getBase64(url, base64 => { - el.setAttribute("href", base64); + if (PRODUCTION && cloneEl.getElementById('oceanicPattern')) { + const el = cloneEl.getElementById('oceanicPattern'); + const url = el.getAttribute('href'); + await new Promise((resolve) => { + getBase64(url, (base64) => { + el.setAttribute('href', base64); resolve(); }); }); } // add relief icons - if (cloneEl.getElementById("terrain")) { + if (cloneEl.getElementById('terrain')) { const uniqueElements = new Set(); - const terrainNodes = cloneEl.getElementById("terrain").childNodes; + const terrainNodes = cloneEl.getElementById('terrain').childNodes; for (let i = 0; i < terrainNodes.length; i++) { - const href = terrainNodes[i].getAttribute("href") || terrainNodes[i].getAttribute("xlink:href"); + const href = terrainNodes[i].getAttribute('href') || terrainNodes[i].getAttribute('xlink:href'); uniqueElements.add(href); } - const defsRelief = svgDefs.getElementById("defs-relief"); + const defsRelief = svgDefs.getElementById('defs-relief'); for (const terrain of [...uniqueElements]) { const element = defsRelief.querySelector(terrain); if (element) cloneDefs.appendChild(element.cloneNode(true)); @@ -234,47 +236,51 @@ async function getMapURL(type, subtype) { } // add wind rose - if (cloneEl.getElementById("compass")) { - const rose = svgDefs.getElementById("rose"); + if (cloneEl.getElementById('compass')) { + const rose = svgDefs.getElementById('rose'); if (rose) cloneDefs.appendChild(rose.cloneNode(true)); } // add port icon - if (cloneEl.getElementById("anchors")) { - const anchor = svgDefs.getElementById("icon-anchor"); + if (cloneEl.getElementById('anchors')) { + const anchor = svgDefs.getElementById('icon-anchor'); if (anchor) cloneDefs.appendChild(anchor.cloneNode(true)); } // add grid pattern - if (cloneEl.getElementById("gridOverlay")?.hasChildNodes()) { - const type = cloneEl.getElementById("gridOverlay").getAttribute("type"); - const pattern = svgDefs.getElementById("pattern_" + type); + if (cloneEl.getElementById('gridOverlay')?.hasChildNodes()) { + const type = cloneEl.getElementById('gridOverlay').getAttribute('type'); + const pattern = svgDefs.getElementById('pattern_' + type); if (pattern) cloneDefs.appendChild(pattern.cloneNode(true)); } - if (!cloneEl.getElementById("hatching").children.length) cloneEl.getElementById("hatching").remove(); // remove unused hatching group - if (!cloneEl.getElementById("fogging-cont")) cloneEl.getElementById("fog").remove(); // remove unused fog - if (!cloneEl.getElementById("regions")) cloneEl.getElementById("statePaths").remove(); // removed unused statePaths - if (!cloneEl.getElementById("labels")) cloneEl.getElementById("textPaths").remove(); // removed unused textPaths + if (!cloneEl.getElementById('hatching').children.length) cloneEl.getElementById('hatching').remove(); // remove unused hatching group + if (!cloneEl.getElementById('fogging-cont')) cloneEl.getElementById('fog').remove(); // remove unused fog + if (!cloneEl.getElementById('regions')) cloneEl.getElementById('statePaths').remove(); // removed unused statePaths + if (!cloneEl.getElementById('labels')) cloneEl.getElementById('textPaths').remove(); // removed unused textPaths // add armies style - if (cloneEl.getElementById("armies")) cloneEl.insertAdjacentHTML("afterbegin", ""); + if (cloneEl.getElementById('armies')) + cloneEl.insertAdjacentHTML( + 'afterbegin', + '' + ); // add xlink: for href to support svg1.1 - if (type === "svg") { - cloneEl.querySelectorAll("[href]").forEach(el => { - const href = el.getAttribute("href"); - el.removeAttribute("href"); - el.setAttribute("xlink:href", href); + if (type === 'svg') { + cloneEl.querySelectorAll('[href]').forEach((el) => { + const href = el.getAttribute('href'); + el.removeAttribute('href'); + el.setAttribute('xlink:href', href); }); } const fontStyle = await GFontToDataURI(getFontsToLoad(clone)); // load non-standard fonts - if (fontStyle) clone.select("defs").append("style").text(fontStyle.join("\n")); // add font to style + if (fontStyle) clone.select('defs').append('style').text(fontStyle.join('\n')); // add font to style clone.remove(); const serialized = `` + new XMLSerializer().serializeToString(cloneEl); - const blob = new Blob([serialized], {type: "image/svg+xml;charset=utf-8"}); + const blob = new Blob([serialized], {type: 'image/svg+xml;charset=utf-8'}); const url = window.URL.createObjectURL(blob); window.setTimeout(() => window.URL.revokeObjectURL(url), 5000); return url; @@ -282,70 +288,70 @@ async function getMapURL(type, subtype) { // remove hidden g elements and g elements without children to make downloaded svg smaller in size function removeUnusedElements(clone) { - if (!terrain.selectAll("use").size()) clone.select("#defs-relief").remove(); - if (markers.style("display") === "none") clone.select("#defs-markers").remove(); + if (!terrain.selectAll('use').size()) clone.select('#defs-relief').remove(); + if (markers.style('display') === 'none') clone.select('#defs-markers').remove(); for (let empty = 1; empty; ) { empty = 0; - clone.selectAll("g").each(function () { - if (!this.hasChildNodes() || this.style.display === "none" || this.classList.contains("hidden")) { + clone.selectAll('g').each(function () { + if (!this.hasChildNodes() || this.style.display === 'none' || this.classList.contains('hidden')) { empty++; this.remove(); } - if (this.hasAttribute("display") && this.style.display === "inline") this.removeAttribute("display"); + if (this.hasAttribute('display') && this.style.display === 'inline') this.removeAttribute('display'); }); } } function updateMeshCells(clone) { - const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter(i => grid.cells.h[i] >= 20); + const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter((i) => grid.cells.h[i] >= 20); const scheme = getColorScheme(); - clone.select("#heights").attr("filter", "url(#blur1)"); + clone.select('#heights').attr('filter', 'url(#blur1)'); clone - .select("#heights") - .selectAll("polygon") + .select('#heights') + .selectAll('polygon') .data(data) - .join("polygon") - .attr("points", d => getGridPolygon(d)) - .attr("id", d => "cell" + d) - .attr("stroke", d => getColor(grid.cells.h[d], scheme)); + .join('polygon') + .attr('points', (d) => getGridPolygon(d)) + .attr('id', (d) => 'cell' + d) + .attr('stroke', (d) => getColor(grid.cells.h[d], scheme)); } // for each g element get inline style function inlineStyle(clone) { - const emptyG = clone.append("g").node(); + const emptyG = clone.append('g').node(); const defaultStyles = window.getComputedStyle(emptyG); - clone.selectAll("g, #ruler *, #scaleBar > text").each(function () { + clone.selectAll('g, #ruler *, #scaleBar > text').each(function () { const compStyle = window.getComputedStyle(this); - let style = ""; + let style = ''; for (let i = 0; i < compStyle.length; i++) { const key = compStyle[i]; const value = compStyle.getPropertyValue(key); // Firefox mask hack - if (key === "mask-image" && value !== defaultStyles.getPropertyValue(key)) { + if (key === 'mask-image' && value !== defaultStyles.getPropertyValue(key)) { style += "mask-image: url('#land');"; continue; } - if (key === "cursor") continue; // cursor should be default + if (key === 'cursor') continue; // cursor should be default if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute if (value === defaultStyles.getPropertyValue(key)) continue; - style += key + ":" + value + ";"; + style += key + ':' + value + ';'; } for (const key in compStyle) { const value = compStyle.getPropertyValue(key); - if (key === "cursor") continue; // cursor should be default + if (key === 'cursor') continue; // cursor should be default if (this.hasAttribute(key)) continue; // don't add style if there is the same attribute if (value === defaultStyles.getPropertyValue(key)) continue; - style += key + ":" + value + ";"; + style += key + ':' + value + ';'; } - if (style != "") this.setAttribute("style", style); + if (style != '') this.setAttribute('style', style); }); emptyG.remove(); @@ -353,35 +359,35 @@ function inlineStyle(clone) { // get non-standard fonts used for labels to fetch them from web function getFontsToLoad(clone) { - const webSafe = ["Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"]; // fonts to not fetch + const webSafe = ['Georgia', 'Times+New+Roman', 'Comic+Sans+MS', 'Lucida+Sans+Unicode', 'Courier+New', 'Verdana', 'Arial', 'Impact']; // fonts to not fetch const fontsInUse = new Set(); // to store fonts currently in use - clone.selectAll("#labels > g").each(function () { + clone.selectAll('#labels > g').each(function () { if (!this.hasChildNodes()) return; const font = this.dataset.font; if (!font || webSafe.includes(font)) return; fontsInUse.add(font); }); - const legendFont = legend.attr("data-font"); + const legendFont = legend.attr('data-font'); if (legend.node().hasChildNodes() && !webSafe.includes(legendFont)) fontsInUse.add(legendFont); const fonts = [...fontsInUse]; - return fonts.length ? "https://fonts.googleapis.com/css?family=" + fonts.join("|") : null; + return fonts.length ? 'https://fonts.googleapis.com/css?family=' + fonts.join('|') : null; } // code from Kaiido's answer https://stackoverflow.com/questions/42402584/how-to-use-google-fonts-in-canvas-when-drawing-dom-objects-in-svg function GFontToDataURI(url) { if (!url) return Promise.resolve(); return fetch(url) // first fecth the embed stylesheet page - .then(resp => resp.text()) // we only need the text of it - .then(text => { - let s = document.createElement("style"); + .then((resp) => resp.text()) // we only need the text of it + .then((text) => { + let s = document.createElement('style'); s.innerHTML = text; document.head.appendChild(s); - const styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0]; + const styleSheet = Array.prototype.filter.call(document.styleSheets, (sS) => sS.ownerNode === s)[0]; - const FontRule = rule => { - const src = rule.style.getPropertyValue("src"); - const url = src ? src.split("url(")[1].split(")")[0] : ""; + const FontRule = (rule) => { + const src = rule.style.getPropertyValue('src'); + const url = src ? src.split('url(')[1].split(')')[0] : ''; return {rule, src, url: url.substring(url.length - 1, 1)}; }; const fontProms = []; @@ -392,15 +398,15 @@ function GFontToDataURI(url) { fontProms.push( fetch(fR.url) // fetch the actual font-file (.woff) - .then(resp => resp.blob()) - .then(blob => { - return new Promise(resolve => { + .then((resp) => resp.blob()) + .then((blob) => { + return new Promise((resolve) => { let f = new FileReader(); - f.onload = e => resolve(f.result); + f.onload = (e) => resolve(f.result); f.readAsDataURL(blob); }); }) - .then(dataURL => fR.rule.cssText.replace(fR.url, dataURL)) + .then((dataURL) => fR.rule.cssText.replace(fR.url, dataURL)) ); } document.head.removeChild(s); // clean up @@ -410,29 +416,52 @@ function GFontToDataURI(url) { // prepare map data for saving function getMapData() { - TIME && console.time("createMapDataBlob"); + TIME && console.time('createMapDataBlob'); - return new Promise(resolve => { + return new Promise((resolve) => { const date = new Date(); - const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); - const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator"; - const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|"); - const settings = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value, heightUnit.value, heightExponentInput.value, temperatureScale.value, barSizeInput.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate, urbanization, mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value, temperaturePoleOutput.value, precOutput.value, JSON.stringify(options), mapName.value, +hideLabels.checked].join("|"); + const dateString = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate(); + const license = 'File can be loaded in azgaar.github.io/Fantasy-Map-Generator'; + const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join('|'); + const settings = [ + distanceUnitInput.value, + distanceScaleInput.value, + areaUnit.value, + heightUnit.value, + heightExponentInput.value, + temperatureScale.value, + barSizeInput.value, + barLabel.value, + barBackOpacity.value, + barBackColor.value, + barPosX.value, + barPosY.value, + populationRate, + urbanization, + mapSizeOutput.value, + latitudeOutput.value, + temperatureEquatorOutput.value, + temperaturePoleOutput.value, + precOutput.value, + JSON.stringify(options), + mapName.value, + +hideLabels.checked + ].join('|'); const coords = JSON.stringify(mapCoordinates); - const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); + const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join('|'); const notesData = JSON.stringify(notes); const rulersString = rulers.toString(); // clone svg - const cloneEl = document.getElementById("map").cloneNode(true); + const cloneEl = document.getElementById('map').cloneNode(true); // set transform values to default - cloneEl.setAttribute("width", graphWidth); - cloneEl.setAttribute("height", graphHeight); - cloneEl.querySelector("#viewbox").removeAttribute("transform"); + cloneEl.setAttribute('width', graphWidth); + cloneEl.setAttribute('height', graphHeight); + cloneEl.querySelector('#viewbox').removeAttribute('transform'); // always remove rulers - cloneEl.querySelector("#ruler").innerHTML = ""; + cloneEl.querySelector('#ruler').innerHTML = ''; const svg_xml = new XMLSerializer().serializeToString(cloneEl); @@ -444,53 +473,91 @@ function getMapData() { const religions = JSON.stringify(pack.religions); const provinces = JSON.stringify(pack.provinces); const rivers = JSON.stringify(pack.rivers); + const resources = JSON.stringify(pack.resources); // store name array only if it is not the same as default const defaultNB = Names.getNameBases(); const namesData = nameBases .map((b, i) => { - const names = defaultNB[i] && defaultNB[i].b === b.b ? "" : b.b; + const names = defaultNB[i] && defaultNB[i].b === b.b ? '' : b.b; return `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${names}`; }) - .join("/"); + .join('/'); - // round population to save resources - const pop = Array.from(pack.cells.pop).map(p => rn(p, 4)); + // round population to save space + const pop = Array.from(pack.cells.pop).map((p) => rn(p, 4)); // data format as below - const data = [params, settings, coords, biomes, notesData, svg_xml, gridGeneral, grid.cells.h, grid.cells.prec, grid.cells.f, grid.cells.t, grid.cells.temp, features, cultures, states, burgs, pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl, pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state, pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces, namesData, rivers, rulersString].join("\r\n"); - const blob = new Blob([data], {type: "text/plain"}); + const data = [ + params, + settings, + coords, + biomes, + notesData, + svg_xml, + gridGeneral, + grid.cells.h, + grid.cells.prec, + grid.cells.f, + grid.cells.t, + grid.cells.temp, + features, + cultures, + states, + burgs, + pack.cells.biome, + pack.cells.burg, + pack.cells.conf, + pack.cells.culture, + pack.cells.fl, + pop, + pack.cells.r, + pack.cells.road, + pack.cells.s, + pack.cells.state, + pack.cells.religion, + pack.cells.province, + pack.cells.crossroad, + religions, + provinces, + namesData, + rivers, + rulersString, + pack.cells.resource, + resources + ].join('\r\n'); + const blob = new Blob([data], {type: 'text/plain'}); - TIME && console.timeEnd("createMapDataBlob"); + TIME && console.timeEnd('createMapDataBlob'); resolve(blob); }); } // Download .map file async function saveMap() { - if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); - closeDialogs("#alert"); + if (customization) return tip('Map cannot be saved when edit mode is active, please exit the mode and retry', false, 'error'); + closeDialogs('#alert'); const blob = await getMapData(); const URL = window.URL.createObjectURL(blob); - const link = document.createElement("a"); - link.download = getFileName() + ".map"; + const link = document.createElement('a'); + link.download = getFileName() + '.map'; link.href = URL; link.click(); - tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); + tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, 'success', 7000); window.URL.revokeObjectURL(URL); } function saveGeoJSON_Cells() { - const json = {type: "FeatureCollection", features: []}; + const json = {type: 'FeatureCollection', features: []}; const cells = pack.cells; - const getPopulation = i => { + const getPopulation = (i) => { const [r, u] = getCellPopulation(i); return rn(r + u); }; - const getHeight = i => parseInt(getFriendlyHeight([cells.p[i][0], cells.p[i][1]])); + const getHeight = (i) => parseInt(getFriendlyHeight([cells.p[i][0], cells.p[i][1]])); - cells.i.forEach(i => { + cells.i.forEach((i) => { const coordinates = getCellCoordinates(cells.v[i]); const height = getHeight(i); const biome = cells.biome[i]; @@ -503,75 +570,75 @@ function saveGeoJSON_Cells() { const neighbors = cells.c[i]; const properties = {id: i, height, biome, type, population, state, province, culture, religion, neighbors}; - const feature = {type: "Feature", geometry: {type: "Polygon", coordinates}, properties}; + const feature = {type: 'Feature', geometry: {type: 'Polygon', coordinates}, properties}; json.features.push(feature); }); - const name = getFileName("Cells") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); + const name = getFileName('Cells') + '.geojson'; + downloadFile(JSON.stringify(json), name, 'application/json'); } function saveGeoJSON_Routes() { - const json = {type: "FeatureCollection", features: []}; + const json = {type: 'FeatureCollection', features: []}; - routes.selectAll("g > path").each(function () { + routes.selectAll('g > path').each(function () { const coordinates = getRoutePoints(this); const id = this.id; const type = this.parentElement.id; - const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, type}}; + const feature = {type: 'Feature', geometry: {type: 'LineString', coordinates}, properties: {id, type}}; json.features.push(feature); }); - const name = getFileName("Routes") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); + const name = getFileName('Routes') + '.geojson'; + downloadFile(JSON.stringify(json), name, 'application/json'); } function saveGeoJSON_Rivers() { - const json = {type: "FeatureCollection", features: []}; + const json = {type: 'FeatureCollection', features: []}; - rivers.selectAll("path").each(function () { + rivers.selectAll('path').each(function () { const coordinates = getRiverPoints(this); const id = this.id; const width = +this.dataset.increment; const increment = +this.dataset.increment; - const river = pack.rivers.find(r => r.i === +id.slice(5)); - const name = river ? river.name : ""; - const type = river ? river.type : ""; - const i = river ? river.i : ""; - const basin = river ? river.basin : ""; + const river = pack.rivers.find((r) => r.i === +id.slice(5)); + const name = river ? river.name : ''; + const type = river ? river.type : ''; + const i = river ? river.i : ''; + const basin = river ? river.basin : ''; - const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, i, basin, name, type, width, increment}}; + const feature = {type: 'Feature', geometry: {type: 'LineString', coordinates}, properties: {id, i, basin, name, type, width, increment}}; json.features.push(feature); }); - const name = getFileName("Rivers") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); + const name = getFileName('Rivers') + '.geojson'; + downloadFile(JSON.stringify(json), name, 'application/json'); } function saveGeoJSON_Markers() { - const json = {type: "FeatureCollection", features: []}; + const json = {type: 'FeatureCollection', features: []}; - markers.selectAll("use").each(function () { + markers.selectAll('use').each(function () { const coordinates = getQGIScoordinates(this.dataset.x, this.dataset.y); const id = this.id; const type = this.dataset.id.substring(1); const icon = document.getElementById(type).textContent; - const note = notes.length ? notes.find(note => note.id === this.id) : null; - const name = note ? note.name : ""; - const legend = note ? note.legend : ""; + const note = notes.length ? notes.find((note) => note.id === this.id) : null; + const name = note ? note.name : ''; + const legend = note ? note.legend : ''; - const feature = {type: "Feature", geometry: {type: "Point", coordinates}, properties: {id, type, icon, name, legend}}; + const feature = {type: 'Feature', geometry: {type: 'Point', coordinates}, properties: {id, type, icon, name, legend}}; json.features.push(feature); }); - const name = getFileName("Markers") + ".geojson"; - downloadFile(JSON.stringify(json), name, "application/json"); + const name = getFileName('Markers') + '.geojson'; + downloadFile(JSON.stringify(json), name, 'application/json'); } function getCellCoordinates(vertices) { const p = pack.vertices.p; - const coordinates = vertices.map(n => getQGIScoordinates(p[n][0], p[n][1])); + const coordinates = vertices.map((n) => getQGIScoordinates(p[n][0], p[n][1])); return [coordinates.concat([coordinates[0]])]; } @@ -601,21 +668,30 @@ function getRiverPoints(node) { async function quickSave() { if (customization) { - tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); + tip('Map cannot be saved when edit mode is active, please exit the mode and retry', false, 'error'); return; } const blob = await getMapData(); - if (blob) ldb.set("lastMap", blob); // auto-save map - tip("Map is saved to browser memory. Please also save as .map file to secure progress", true, "success", 2000); + if (blob) ldb.set('lastMap', blob); // auto-save map + tip('Map is saved to browser memory. Please also save as .map file to secure progress', true, 'success', 2000); } const saveReminder = function () { - if (localStorage.getItem("noReminder")) return; - const message = ["Please don't forget to save your work as a .map file", "Please remember to save work as a .map file", "Saving in .map format will ensure your data won't be lost in case of issues", "Safety is number one priority. Please save the map", "Don't forget to save your map on a regular basis!", "Just a gentle reminder for you to save the map", "Please don't forget to save your progress (saving as .map is the best option)", "Don't want to be reminded about need to save? Press CTRL+Q"]; + if (localStorage.getItem('noReminder')) return; + const message = [ + "Please don't forget to save your work as a .map file", + 'Please remember to save work as a .map file', + "Saving in .map format will ensure your data won't be lost in case of issues", + 'Safety is number one priority. Please save the map', + "Don't forget to save your map on a regular basis!", + 'Just a gentle reminder for you to save the map', + "Please don't forget to save your progress (saving as .map is the best option)", + "Don't want to be reminded about need to save? Press CTRL+Q" + ]; saveReminder.reminder = setInterval(() => { if (customization) return; - tip(ra(message), true, "warn", 2500); + tip(ra(message), true, 'warn', 2500); }, 1e6); saveReminder.status = 1; }; @@ -624,13 +700,13 @@ saveReminder(); function toggleSaveReminder() { if (saveReminder.status) { - tip("Save reminder is turned off. Press CTRL+Q again to re-initiate", true, "warn", 2000); + tip('Save reminder is turned off. Press CTRL+Q again to re-initiate', true, 'warn', 2000); clearInterval(saveReminder.reminder); - localStorage.setItem("noReminder", true); + localStorage.setItem('noReminder', true); saveReminder.status = 0; } else { - tip("Save reminder is turned on. Press CTRL+Q to turn off", true, "warn", 2000); - localStorage.removeItem("noReminder"); + tip('Save reminder is turned on. Press CTRL+Q to turn off', true, 'warn', 2000); + localStorage.removeItem('noReminder'); saveReminder(); } } diff --git a/modules/ui/layers.js b/modules/ui/layers.js index bf8ff29d..4b89a8d2 100644 --- a/modules/ui/layers.js +++ b/modules/ui/layers.js @@ -14,6 +14,7 @@ function getDefaultPresets() { heightmap: ['toggleHeight', 'toggleRivers'], physical: ['toggleCoordinates', 'toggleHeight', 'toggleIce', 'toggleRivers', 'toggleScaleBar'], poi: ['toggleBorders', 'toggleHeight', 'toggleIce', 'toggleIcons', 'toggleMarkers', 'toggleRivers', 'toggleRoutes', 'toggleScaleBar'], + economical: ['toggleResources', 'toggleBiomes', 'toggleBorders', 'toggleIcons', 'toggleIce', 'toggleLabels', 'toggleRivers', 'toggleRoutes', 'toggleScaleBar'], military: ['toggleBorders', 'toggleIcons', 'toggleLabels', 'toggleMilitary', 'toggleRivers', 'toggleRoutes', 'toggleScaleBar', 'toggleStates'], emblems: ['toggleBorders', 'toggleIcons', 'toggleIce', 'toggleEmblems', 'toggleRivers', 'toggleRoutes', 'toggleScaleBar', 'toggleStates'], landmass: ['toggleScaleBar'] diff --git a/modules/ui/style.js b/modules/ui/style.js index 528b0fa3..03ce684f 100644 --- a/modules/ui/style.js +++ b/modules/ui/style.js @@ -2,7 +2,7 @@ 'use strict'; // store some style inputs as options -styleElements.addEventListener("change", function (ev) { +styleElements.addEventListener('change', function (ev) { if (ev.target.dataset.stored) lock(ev.target.dataset.stored); }); @@ -26,24 +26,24 @@ function editStyle(element, group) { styleElementSelect.addEventListener('change', selectStyleElement); function selectStyleElement() { const sel = styleElementSelect.value; - let el = d3.select("#" + sel); + let el = d3.select('#' + sel); - styleElements.querySelectorAll("tbody").forEach(e => (e.style.display = "none")); // hide all sections - const off = sel !== "ocean" && (el.style("display") === "none" || !el.selectAll("*").size()); // check if layer is off + styleElements.querySelectorAll('tbody').forEach((e) => (e.style.display = 'none')); // hide all sections + const off = sel !== 'ocean' && (el.style('display') === 'none' || !el.selectAll('*').size()); // check if layer is off if (off) { - styleIsOff.style.display = "block"; - setTimeout(() => (styleIsOff.style.display = "none"), 1500); + styleIsOff.style.display = 'block'; + setTimeout(() => (styleIsOff.style.display = 'none'), 1500); } // active group element const group = styleGroupSelect.value; - if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(sel)) { + if (['routes', 'labels', 'coastline', 'lakes', 'anchors', 'burgIcons', 'borders'].includes(sel)) { el = d3 - .select("#" + sel) - .select("g#" + group) + .select('#' + sel) + .select('g#' + group) .size() - ? d3.select("#" + sel).select("g#" + group) - : d3.select("#" + sel).select("g"); + ? d3.select('#' + sel).select('g#' + group) + : d3.select('#' + sel).select('g'); } if (sel !== 'landmass' && sel !== 'legend') { @@ -86,16 +86,16 @@ function selectStyleElement() { } // show specific sections - if (sel === "texture") styleTexture.style.display = "block"; - if ((sel === "routes", "labels" || sel == "anchors" || sel == "burgIcons", "coastline", "lakes", "borders")) styleGroup.style.display = "block"; + if (sel === 'texture') styleTexture.style.display = 'block'; + if ((sel === 'routes', 'labels' || sel == 'anchors' || sel == 'burgIcons', 'coastline', 'lakes', 'borders')) styleGroup.style.display = 'block'; - if (sel === "terrs") { - styleHeightmap.style.display = "block"; - styleHeightmapScheme.value = terrs.attr("scheme"); - styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = terrs.attr("terracing"); - styleHeightmapSkipInput.value = styleHeightmapSkipOutput.value = terrs.attr("skip"); - styleHeightmapSimplificationInput.value = styleHeightmapSimplificationOutput.value = terrs.attr("relax"); - styleHeightmapCurve.value = terrs.attr("curve"); + if (sel === 'terrs') { + styleHeightmap.style.display = 'block'; + styleHeightmapScheme.value = terrs.attr('scheme'); + styleHeightmapTerracingInput.value = styleHeightmapTerracingOutput.value = terrs.attr('terracing'); + styleHeightmapSkipInput.value = styleHeightmapSkipOutput.value = terrs.attr('skip'); + styleHeightmapSimplificationInput.value = styleHeightmapSimplificationOutput.value = terrs.attr('relax'); + styleHeightmapCurve.value = terrs.attr('curve'); } if (sel === 'markers') { @@ -146,18 +146,18 @@ function selectStyleElement() { styleStroke.style.display = 'block'; styleStrokeWidth.style.display = 'block'; loadDefaultFonts(); - styleFont.style.display = "block"; - styleShadow.style.display = "block"; - styleSize.style.display = "block"; - styleVisibility.style.display = "block"; - styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#3e3e4b"; - styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#3a3a3a"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || 0; - styleShadowInput.value = el.style("text-shadow") || "white 0 0 4px"; - styleSelectFont.value = fonts.indexOf(el.attr("data-font")); - styleInputFont.style.display = "none"; - styleInputFont.value = ""; - styleFontSize.value = el.attr("data-size"); + styleFont.style.display = 'block'; + styleShadow.style.display = 'block'; + styleSize.style.display = 'block'; + styleVisibility.style.display = 'block'; + styleFillInput.value = styleFillOutput.value = el.attr('fill') || '#3e3e4b'; + styleStrokeInput.value = styleStrokeOutput.value = el.attr('stroke') || '#3a3a3a'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 0; + styleShadowInput.value = el.style('text-shadow') || 'white 0 0 4px'; + styleSelectFont.value = fonts.indexOf(el.attr('data-font')); + styleInputFont.style.display = 'none'; + styleInputFont.value = ''; + styleFontSize.value = el.attr('data-size'); } if (sel === 'provs') { @@ -172,66 +172,66 @@ function selectStyleElement() { styleFontSize.value = el.attr('data-size'); } - if (sel == "burgIcons") { - styleFill.style.display = "block"; - styleStroke.style.display = "block"; - styleStrokeWidth.style.display = "block"; - styleStrokeDash.style.display = "block"; - styleRadius.style.display = "block"; - styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#ffffff"; - styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#3e3e4b"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || 0.24; - styleStrokeDasharrayInput.value = el.attr("stroke-dasharray") || ""; - styleStrokeLinecapInput.value = el.attr("stroke-linecap") || "inherit"; - styleRadiusInput.value = el.attr("size") || 1; + if (sel == 'burgIcons') { + styleFill.style.display = 'block'; + styleStroke.style.display = 'block'; + styleStrokeWidth.style.display = 'block'; + styleStrokeDash.style.display = 'block'; + styleRadius.style.display = 'block'; + styleFillInput.value = styleFillOutput.value = el.attr('fill') || '#ffffff'; + styleStrokeInput.value = styleStrokeOutput.value = el.attr('stroke') || '#3e3e4b'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 0.24; + styleStrokeDasharrayInput.value = el.attr('stroke-dasharray') || ''; + styleStrokeLinecapInput.value = el.attr('stroke-linecap') || 'inherit'; + styleRadiusInput.value = el.attr('size') || 1; } - if (sel == "anchors") { - styleFill.style.display = "block"; - styleStroke.style.display = "block"; - styleStrokeWidth.style.display = "block"; - styleIconSize.style.display = "block"; - styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#ffffff"; - styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#3e3e4b"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || 0.24; - styleIconSizeInput.value = el.attr("size") || 2; + if (sel == 'anchors') { + styleFill.style.display = 'block'; + styleStroke.style.display = 'block'; + styleStrokeWidth.style.display = 'block'; + styleIconSize.style.display = 'block'; + styleFillInput.value = styleFillOutput.value = el.attr('fill') || '#ffffff'; + styleStrokeInput.value = styleStrokeOutput.value = el.attr('stroke') || '#3e3e4b'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 0.24; + styleIconSizeInput.value = el.attr('size') || 2; } if (sel === 'legend') { styleStroke.style.display = 'block'; styleStrokeWidth.style.display = 'block'; loadDefaultFonts(); - styleFont.style.display = "block"; - styleSize.style.display = "block"; + styleFont.style.display = 'block'; + styleSize.style.display = 'block'; - styleLegend.style.display = "block"; - styleLegendColItemsOutput.value = styleLegendColItems.value = el.attr("data-columns"); - styleLegendBackOutput.value = styleLegendBack.value = el.select("#legendBox").attr("fill"); - styleLegendOpacityOutput.value = styleLegendOpacity.value = el.select("#legendBox").attr("fill-opacity"); + styleLegend.style.display = 'block'; + styleLegendColItemsOutput.value = styleLegendColItems.value = el.attr('data-columns'); + styleLegendBackOutput.value = styleLegendBack.value = el.select('#legendBox').attr('fill'); + styleLegendOpacityOutput.value = styleLegendOpacity.value = el.select('#legendBox').attr('fill-opacity'); - styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#111111"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || 0.5; - styleSelectFont.value = fonts.indexOf(el.attr("data-font")); - styleInputFont.style.display = "none"; - styleInputFont.value = ""; - styleFontSize.value = el.attr("data-size"); + styleStrokeInput.value = styleStrokeOutput.value = el.attr('stroke') || '#111111'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 0.5; + styleSelectFont.value = fonts.indexOf(el.attr('data-font')); + styleInputFont.style.display = 'none'; + styleInputFont.value = ''; + styleFontSize.value = el.attr('data-size'); } - if (sel === "ocean") { - styleOcean.style.display = "block"; - styleOceanFill.value = styleOceanFillOutput.value = oceanLayers.select("#oceanBase").attr("fill"); - styleOceanPattern.value = document.getElementById("oceanicPattern")?.getAttribute("href"); - styleOceanPatternOpacity.value = styleOceanPatternOpacityOutput.value = document.getElementById("oceanicPattern").getAttribute("opacity") || 1; - outlineLayers.value = oceanLayers.attr("layers"); + if (sel === 'ocean') { + styleOcean.style.display = 'block'; + styleOceanFill.value = styleOceanFillOutput.value = oceanLayers.select('#oceanBase').attr('fill'); + styleOceanPattern.value = document.getElementById('oceanicPattern')?.getAttribute('href'); + styleOceanPatternOpacity.value = styleOceanPatternOpacityOutput.value = document.getElementById('oceanicPattern').getAttribute('opacity') || 1; + outlineLayers.value = oceanLayers.attr('layers'); } - if (sel === "temperature") { - styleStrokeWidth.style.display = "block"; - styleTemperature.style.display = "block"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || ""; - styleTemperatureFillOpacityInput.value = styleTemperatureFillOpacityOutput.value = el.attr("fill-opacity") || 0.1; - styleTemperatureFillInput.value = styleTemperatureFillOutput.value = el.attr("fill") || "#000"; - styleTemperatureFontSizeInput.value = styleTemperatureFontSizeOutput.value = el.attr("font-size") || "8px"; + if (sel === 'temperature') { + styleStrokeWidth.style.display = 'block'; + styleTemperature.style.display = 'block'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || ''; + styleTemperatureFillOpacityInput.value = styleTemperatureFillOpacityOutput.value = el.attr('fill-opacity') || 0.1; + styleTemperatureFillInput.value = styleTemperatureFillOutput.value = el.attr('fill') || '#000'; + styleTemperatureFontSizeInput.value = styleTemperatureFontSizeOutput.value = el.attr('font-size') || '8px'; } if (sel === 'coordinates') { @@ -253,24 +253,24 @@ function selectStyleElement() { // update group options styleGroupSelect.options.length = 0; // remove all options - if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(sel)) { + if (['routes', 'labels', 'coastline', 'lakes', 'anchors', 'burgIcons', 'borders'].includes(sel)) { document .getElementById(sel) - .querySelectorAll("g") - .forEach(el => { - if (el.id === "burgLabels") return; + .querySelectorAll('g') + .forEach((el) => { + if (el.id === 'burgLabels') return; const count = el.childElementCount; styleGroupSelect.options.add(new Option(`${el.id} (${count})`, el.id, false, false)); }); - styleGroupSelect.value = el.attr("id"); + styleGroupSelect.value = el.attr('id'); } else { styleGroupSelect.options.add(new Option(sel, sel, false, true)); } - if (sel === "coastline" && styleGroupSelect.value === "sea_island") { - styleCoastline.style.display = "block"; - const auto = (styleCoastlineAuto.checked = coastline.select("#sea_island").attr("auto-filter")); - if (auto) styleFilter.style.display = "none"; + if (sel === 'coastline' && styleGroupSelect.value === 'sea_island') { + styleCoastline.style.display = 'block'; + const auto = (styleCoastlineAuto.checked = coastline.select('#sea_island').attr('auto-filter')); + if (auto) styleFilter.style.display = 'none'; } } @@ -280,83 +280,83 @@ styleGroupSelect.addEventListener('change', selectStyleElement); function getEl() { const el = styleElementSelect.value, g = styleGroupSelect.value; - if (g === el) return svg.select("#" + el); - else return svg.select("#" + el).select("#" + g); + if (g === el) return svg.select('#' + el); + else return svg.select('#' + el).select('#' + g); } -styleFillInput.addEventListener("input", function () { +styleFillInput.addEventListener('input', function () { styleFillOutput.value = this.value; - getEl().attr("fill", this.value); + getEl().attr('fill', this.value); }); -styleStrokeInput.addEventListener("input", function () { +styleStrokeInput.addEventListener('input', function () { styleStrokeOutput.value = this.value; - getEl().attr("stroke", this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); + getEl().attr('stroke', this.value); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleStrokeWidthInput.addEventListener("input", function () { +styleStrokeWidthInput.addEventListener('input', function () { styleStrokeWidthOutput.value = this.value; - getEl().attr("stroke-width", +this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); + getEl().attr('stroke-width', +this.value); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleStrokeDasharrayInput.addEventListener("input", function () { - getEl().attr("stroke-dasharray", this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); +styleStrokeDasharrayInput.addEventListener('input', function () { + getEl().attr('stroke-dasharray', this.value); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleStrokeLinecapInput.addEventListener("change", function () { - getEl().attr("stroke-linecap", this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); +styleStrokeLinecapInput.addEventListener('change', function () { + getEl().attr('stroke-linecap', this.value); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleOpacityInput.addEventListener("input", function () { +styleOpacityInput.addEventListener('input', function () { styleOpacityOutput.value = this.value; - getEl().attr("opacity", this.value); + getEl().attr('opacity', this.value); }); -styleFilterInput.addEventListener("change", function () { - if (styleGroupSelect.value === "ocean") { - oceanLayers.attr("filter", this.value); +styleFilterInput.addEventListener('change', function () { + if (styleGroupSelect.value === 'ocean') { + oceanLayers.attr('filter', this.value); return; } - getEl().attr("filter", this.value); + getEl().attr('filter', this.value); }); -styleTextureInput.addEventListener("change", function () { - if (this.value === "none") texture.select("image").attr("xlink:href", ""); - if (this.value === "default") texture.select("image").attr("xlink:href", getDefaultTexture()); - else getBase64(this.value, base64 => texture.select("image").attr("xlink:href", base64)); +styleTextureInput.addEventListener('change', function () { + if (this.value === 'none') texture.select('image').attr('xlink:href', ''); + if (this.value === 'default') texture.select('image').attr('xlink:href', getDefaultTexture()); + else getBase64(this.value, (base64) => texture.select('image').attr('xlink:href', base64)); }); -styleTextureShiftX.addEventListener("input", function () { +styleTextureShiftX.addEventListener('input', function () { texture - .select("image") - .attr("x", this.value) - .attr("width", graphWidth - this.valueAsNumber); + .select('image') + .attr('x', this.value) + .attr('width', graphWidth - this.valueAsNumber); }); -styleTextureShiftY.addEventListener("input", function () { +styleTextureShiftY.addEventListener('input', function () { texture - .select("image") - .attr("y", this.value) - .attr("height", graphHeight - this.valueAsNumber); + .select('image') + .attr('y', this.value) + .attr('height', graphHeight - this.valueAsNumber); }); -styleClippingInput.addEventListener("change", function () { - getEl().attr("mask", this.value); +styleClippingInput.addEventListener('change', function () { + getEl().attr('mask', this.value); }); -styleGridType.addEventListener("change", function () { - getEl().attr("type", this.value); - if (layerIsOn("toggleGrid")) drawGrid(); +styleGridType.addEventListener('change', function () { + getEl().attr('type', this.value); + if (layerIsOn('toggleGrid')) drawGrid(); calculateFriendlyGridSize(); }); -styleGridScale.addEventListener("input", function () { - getEl().attr("scale", this.value); - if (layerIsOn("toggleGrid")) drawGrid(); +styleGridScale.addEventListener('input', function () { + getEl().attr('scale', this.value); + if (layerIsOn('toggleGrid')) drawGrid(); calculateFriendlyGridSize(); }); @@ -366,14 +366,14 @@ function calculateFriendlyGridSize() { styleGridSizeFriendly.value = friendly; } -styleGridShiftX.addEventListener("input", function () { - getEl().attr("dx", this.value); - if (layerIsOn("toggleGrid")) drawGrid(); +styleGridShiftX.addEventListener('input', function () { + getEl().attr('dx', this.value); + if (layerIsOn('toggleGrid')) drawGrid(); }); -styleGridShiftY.addEventListener("input", function () { - getEl().attr("dy", this.value); - if (layerIsOn("toggleGrid")) drawGrid(); +styleGridShiftY.addEventListener('input', function () { + getEl().attr('dy', this.value); + if (layerIsOn('toggleGrid')) drawGrid(); }); styleShiftX.addEventListener('input', shiftElement); @@ -385,118 +385,118 @@ function shiftElement() { getEl().attr('transform', `translate(${x},${y})`); } -styleRescaleMarkers.addEventListener("change", function () { - markers.attr("rescale", +this.checked); +styleRescaleMarkers.addEventListener('change', function () { + markers.attr('rescale', +this.checked); invokeActiveZooming(); }); -styleCoastlineAuto.addEventListener("change", function () { - coastline.select("#sea_island").attr("auto-filter", +this.checked); - styleFilter.style.display = this.checked ? "none" : "block"; +styleCoastlineAuto.addEventListener('change', function () { + coastline.select('#sea_island').attr('auto-filter', +this.checked); + styleFilter.style.display = this.checked ? 'none' : 'block'; invokeActiveZooming(); }); -styleOceanFill.addEventListener("input", function () { - oceanLayers.select("rect").attr("fill", this.value); +styleOceanFill.addEventListener('input', function () { + oceanLayers.select('rect').attr('fill', this.value); styleOceanFillOutput.value = this.value; }); -styleOceanPattern.addEventListener("change", function () { - document.getElementById("oceanicPattern")?.setAttribute("href", this.value); +styleOceanPattern.addEventListener('change', function () { + document.getElementById('oceanicPattern')?.setAttribute('href', this.value); }); -styleOceanPatternOpacity.addEventListener("input", function () { - document.getElementById("oceanicPattern").setAttribute("opacity", this.value); +styleOceanPatternOpacity.addEventListener('input', function () { + document.getElementById('oceanicPattern').setAttribute('opacity', this.value); styleOceanPatternOpacityOutput.value = this.value; }); -outlineLayers.addEventListener("change", function () { - oceanLayers.selectAll("path").remove(); - oceanLayers.attr("layers", this.value); +outlineLayers.addEventListener('change', function () { + oceanLayers.selectAll('path').remove(); + oceanLayers.attr('layers', this.value); OceanLayers(); }); -styleHeightmapScheme.addEventListener("change", function () { - terrs.attr("scheme", this.value); +styleHeightmapScheme.addEventListener('change', function () { + terrs.attr('scheme', this.value); drawHeightmap(); }); -styleHeightmapTerracingInput.addEventListener("input", function () { - terrs.attr("terracing", this.value); +styleHeightmapTerracingInput.addEventListener('input', function () { + terrs.attr('terracing', this.value); drawHeightmap(); }); -styleHeightmapSkipInput.addEventListener("input", function () { - terrs.attr("skip", this.value); +styleHeightmapSkipInput.addEventListener('input', function () { + terrs.attr('skip', this.value); drawHeightmap(); }); -styleHeightmapSimplificationInput.addEventListener("input", function () { - terrs.attr("relax", this.value); +styleHeightmapSimplificationInput.addEventListener('input', function () { + terrs.attr('relax', this.value); drawHeightmap(); }); -styleHeightmapCurve.addEventListener("change", function () { - terrs.attr("curve", this.value); +styleHeightmapCurve.addEventListener('change', function () { + terrs.attr('curve', this.value); drawHeightmap(); }); -styleReliefSet.addEventListener("change", function () { - terrain.attr("set", this.value); +styleReliefSet.addEventListener('change', function () { + terrain.attr('set', this.value); ReliefIcons(); if (!layerIsOn('toggleRelief')) toggleRelief(); }); -styleReliefSizeInput.addEventListener("change", function () { +styleReliefSizeInput.addEventListener('change', function () { styleReliefSizeOutput.value = this.value; const size = +this.value; - terrain.attr("size", size); + terrain.attr('size', size); - terrain.selectAll("use").each(function (d) { - const newSize = this.getAttribute("data-size") * size; - const shift = (newSize - +this.getAttribute("width")) / 2; - this.setAttribute("width", newSize); - this.setAttribute("height", newSize); - const x = +this.getAttribute("x"); - const y = +this.getAttribute("y"); - this.setAttribute("x", x - shift); - this.setAttribute("y", y - shift); + terrain.selectAll('use').each(function (d) { + const newSize = this.getAttribute('data-size') * size; + const shift = (newSize - +this.getAttribute('width')) / 2; + this.setAttribute('width', newSize); + this.setAttribute('height', newSize); + const x = +this.getAttribute('x'); + const y = +this.getAttribute('y'); + this.setAttribute('x', x - shift); + this.setAttribute('y', y - shift); }); }); -styleReliefDensityInput.addEventListener("input", function () { - terrain.attr("density", this.value); +styleReliefDensityInput.addEventListener('input', function () { + terrain.attr('density', this.value); styleReliefDensityOutput.value = this.value; ReliefIcons(); if (!layerIsOn('toggleRelief')) toggleRelief(); }); -styleTemperatureFillOpacityInput.addEventListener("input", function () { - temperature.attr("fill-opacity", this.value); +styleTemperatureFillOpacityInput.addEventListener('input', function () { + temperature.attr('fill-opacity', this.value); styleTemperatureFillOpacityOutput.value = this.value; }); -styleTemperatureFontSizeInput.addEventListener("input", function () { - temperature.attr("font-size", this.value + "px"); - styleTemperatureFontSizeOutput.value = this.value + "px"; +styleTemperatureFontSizeInput.addEventListener('input', function () { + temperature.attr('font-size', this.value + 'px'); + styleTemperatureFontSizeOutput.value = this.value + 'px'; }); -styleTemperatureFillInput.addEventListener("input", function () { - temperature.attr("fill", this.value); +styleTemperatureFillInput.addEventListener('input', function () { + temperature.attr('fill', this.value); styleTemperatureFillOutput.value = this.value; }); -stylePopulationRuralStrokeInput.addEventListener("input", function () { - population.select("#rural").attr("stroke", this.value); +stylePopulationRuralStrokeInput.addEventListener('input', function () { + population.select('#rural').attr('stroke', this.value); stylePopulationRuralStrokeOutput.value = this.value; }); -stylePopulationUrbanStrokeInput.addEventListener("input", function () { - population.select("#urban").attr("stroke", this.value); +stylePopulationUrbanStrokeInput.addEventListener('input', function () { + population.select('#urban').attr('stroke', this.value); stylePopulationUrbanStrokeOutput.value = this.value; }); -styleCompassSizeInput.addEventListener("input", function () { +styleCompassSizeInput.addEventListener('input', function () { styleCompassSizeOutput.value = this.value; shiftCompass(); }); @@ -509,18 +509,18 @@ function shiftCompass() { compass.select('use').attr('transform', tr); } -styleLegendColItems.addEventListener("input", function () { +styleLegendColItems.addEventListener('input', function () { styleLegendColItemsOutput.value = this.value; legend.select('#legendBox').attr('data-columns', this.value); redrawLegend(); }); -styleLegendBack.addEventListener("input", function () { +styleLegendBack.addEventListener('input', function () { styleLegendBackOutput.value = this.value; legend.select('#legendBox').attr('fill', this.value); }); -styleLegendOpacity.addEventListener("input", function () { +styleLegendOpacity.addEventListener('input', function () { styleLegendOpacityOutput.value = this.value; legend.select('#legendBox').attr('fill-opacity', this.value); }); @@ -528,18 +528,18 @@ styleLegendOpacity.addEventListener("input", function () { styleSelectFont.addEventListener('change', changeFont); function changeFont() { const value = styleSelectFont.value; - const font = fonts[value].split(":")[0].replace(/\+/g, " "); - getEl().attr("font-family", font).attr("data-font", fonts[value]); - if (styleElementSelect.value === "legend") redrawLegend(); + const font = fonts[value].split(':')[0].replace(/\+/g, ' '); + getEl().attr('font-family', font).attr('data-font', fonts[value]); + if (styleElementSelect.value === 'legend') redrawLegend(); } -styleShadowInput.addEventListener("input", function () { - getEl().style("text-shadow", this.value); +styleShadowInput.addEventListener('input', function () { + getEl().style('text-shadow', this.value); }); -styleFontAdd.addEventListener("click", function () { - if (styleInputFont.style.display === "none") { - styleInputFont.style.display = "inline-block"; +styleFontAdd.addEventListener('click', function () { + if (styleInputFont.style.display === 'none') { + styleInputFont.style.display = 'inline-block'; styleInputFont.focus(); styleSelectFont.style.display = 'none'; } else { @@ -548,12 +548,12 @@ styleFontAdd.addEventListener("click", function () { } }); -styleInputFont.addEventListener("change", function () { +styleInputFont.addEventListener('change', function () { if (!this.value) { - tip("Please provide a valid Google font name or link to a @font-face declaration"); + tip('Please provide a valid Google font name or link to a @font-face declaration'); return; } - fetchFonts(this.value).then(fetched => { + fetchFonts(this.value).then((fetched) => { if (!fetched) return; styleFontAdd.click(); styleInputFont.value = ''; @@ -563,17 +563,17 @@ styleInputFont.addEventListener("change", function () { }); }); -styleFontSize.addEventListener("change", function () { +styleFontSize.addEventListener('change', function () { changeFontSize(+this.value); }); -styleFontPlus.addEventListener("click", function () { - const size = Math.max(rn(getEl().attr("data-size") * 1.1, 2), 1); +styleFontPlus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('data-size') * 1.1, 2), 1); changeFontSize(size); }); -styleFontMinus.addEventListener("click", function () { - const size = Math.max(rn(getEl().attr("data-size") * 0.9, 2), 1); +styleFontMinus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('data-size') * 0.9, 2), 1); changeFontSize(size); }); @@ -582,89 +582,89 @@ function changeFontSize(size) { const coords = styleElementSelect.value === 'coordinates'; const desSize = legend ? size : coords ? rn(size / scale ** 0.8, 2) : rn(size + size / scale); - getEl().attr("data-size", size).attr("font-size", desSize); + getEl().attr('data-size', size).attr('font-size', desSize); styleFontSize.value = size; if (legend) redrawLegend(); } -styleRadiusInput.addEventListener("change", function () { +styleRadiusInput.addEventListener('change', function () { changeRadius(+this.value); }); -styleRadiusPlus.addEventListener("click", function () { - const size = Math.max(rn(getEl().attr("size") * 1.1, 2), 0.2); +styleRadiusPlus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('size') * 1.1, 2), 0.2); changeRadius(size); }); -styleRadiusMinus.addEventListener("click", function () { - const size = Math.max(rn(getEl().attr("size") * 0.9, 2), 0.2); +styleRadiusMinus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('size') * 0.9, 2), 0.2); changeRadius(size); }); function changeRadius(size, group) { - const el = group ? burgIcons.select("#" + group) : getEl(); - const g = el.attr("id"); - el.attr("size", size); - el.selectAll("circle").each(function () { - this.setAttribute("r", size); + const el = group ? burgIcons.select('#' + group) : getEl(); + const g = el.attr('id'); + el.attr('size', size); + el.selectAll('circle').each(function () { + this.setAttribute('r', size); }); styleRadiusInput.value = size; burgLabels - .select("g#" + g) - .selectAll("text") + .select('g#' + g) + .selectAll('text') .each(function () { - this.setAttribute("dy", `${size * -1.5}px`); + this.setAttribute('dy', `${size * -1.5}px`); }); changeIconSize(size * 2, g); // change also anchor icons } -styleIconSizeInput.addEventListener("change", function () { +styleIconSizeInput.addEventListener('change', function () { changeIconSize(+this.value); }); -styleIconSizePlus.addEventListener("click", function () { - const size = Math.max(rn(getEl().attr("size") * 1.1, 2), 0.2); +styleIconSizePlus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('size') * 1.1, 2), 0.2); changeIconSize(size); }); -styleIconSizeMinus.addEventListener("click", function () { - const size = Math.max(rn(getEl().attr("size") * 0.9, 2), 0.2); +styleIconSizeMinus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('size') * 0.9, 2), 0.2); changeIconSize(size); }); function changeIconSize(size, group) { - const el = group ? anchors.select("#" + group) : getEl(); - const oldSize = +el.attr("size"); + const el = group ? anchors.select('#' + group) : getEl(); + const oldSize = +el.attr('size'); const shift = (size - oldSize) / 2; - el.attr("size", size); - el.selectAll("use").each(function () { - const x = +this.getAttribute("x"); - const y = +this.getAttribute("y"); - this.setAttribute("x", x - shift); - this.setAttribute("y", y - shift); - this.setAttribute("width", size); - this.setAttribute("height", size); + el.attr('size', size); + el.selectAll('use').each(function () { + const x = +this.getAttribute('x'); + const y = +this.getAttribute('y'); + this.setAttribute('x', x - shift); + this.setAttribute('y', y - shift); + this.setAttribute('width', size); + this.setAttribute('height', size); }); styleIconSizeInput.value = size; } -styleStatesHaloWidth.addEventListener("input", function () { +styleStatesHaloWidth.addEventListener('input', function () { styleStatesHaloWidthOutput.value = this.value; statesHalo.attr('data-width', this.value).attr('stroke-width', this.value); }); -styleStatesHaloOpacity.addEventListener("input", function () { +styleStatesHaloOpacity.addEventListener('input', function () { styleStatesHaloOpacityOutput.value = this.value; statesHalo.attr('opacity', this.value); }); -styleArmiesFillOpacity.addEventListener("input", function () { - armies.attr("fill-opacity", this.value); +styleArmiesFillOpacity.addEventListener('input', function () { + armies.attr('fill-opacity', this.value); styleArmiesFillOpacityOutput.value = this.value; }); -styleArmiesSize.addEventListener("input", function () { - armies.attr("box-size", this.value).attr("font-size", this.value * 2); +styleArmiesSize.addEventListener('input', function () { + armies.attr('box-size', this.value).attr('font-size', this.value * 2); styleArmiesSizeOutput.value = this.value; armies.selectAll('g').remove(); // clear armies layer pack.states.forEach((s) => { @@ -682,18 +682,18 @@ function textureProvideURL() { alertMessage.innerHTML = `Provide an image URL to be used as a texture: `; - $("#alert").dialog({ + $('#alert').dialog({ resizable: false, - title: "Load custom texture", - width: "26em", + title: 'Load custom texture', + width: '26em', buttons: { Apply: function () { - const name = textureURL.value.split("/").pop(); - if (!name || name === "") { - tip("Please provide a valid URL", false, "error"); + const name = textureURL.value.split('/').pop(); + if (!name || name === '') { + tip('Please provide a valid URL', false, 'error'); return; } - const opt = document.createElement("option"); + const opt = document.createElement('option'); opt.value = textureURL.value; opt.text = name.slice(0, 20); styleTextureInput.add(opt); @@ -703,7 +703,7 @@ function textureProvideURL() { $(this).dialog('close'); }, Cancel: function () { - $(this).dialog("close"); + $(this).dialog('close'); } } }); @@ -749,84 +749,171 @@ function applyStyleOnLoad() { // set default style function applyDefaultStyle() { - armies.attr("opacity", 1).attr("fill-opacity", 1).attr("font-size", 6).attr("box-size", 3).attr("stroke", "#000").attr("stroke-width", 0.3); + armies.attr('opacity', 1).attr('fill-opacity', 1).attr('font-size', 6).attr('box-size', 3).attr('stroke', '#000').attr('stroke-width', 0.3); - biomes.attr("opacity", null).attr("filter", null).attr("mask", null); - ice.attr("opacity", 0.9).attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("stroke-width", 1).attr("filter", "url(#dropShadow05)"); - stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt").attr("filter", null); - provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "0 2").attr("stroke-linecap", "round").attr("filter", null); - cells.attr("opacity", null).attr("stroke", "#808080").attr("stroke-width", 0.1).attr("filter", null).attr("mask", null); + biomes.attr('opacity', null).attr('filter', null).attr('mask', null); + ice.attr('opacity', 0.9).attr('fill', '#e8f0f6').attr('stroke', '#e8f0f6').attr('stroke-width', 1).attr('filter', 'url(#dropShadow05)'); + stateBorders.attr('opacity', 0.8).attr('stroke', '#56566d').attr('stroke-width', 1).attr('stroke-dasharray', '2').attr('stroke-linecap', 'butt').attr('filter', null); + provinceBorders.attr('opacity', 0.8).attr('stroke', '#56566d').attr('stroke-width', 0.5).attr('stroke-dasharray', '0 2').attr('stroke-linecap', 'round').attr('filter', null); + cells.attr('opacity', null).attr('stroke', '#808080').attr('stroke-width', 0.1).attr('filter', null).attr('mask', null); - gridOverlay.attr("opacity", 0.8).attr("type", "pointyHex").attr("scale", 1).attr("dx", 0).attr("dy", 0).attr("stroke", "#777777").attr("stroke-width", 0.5).attr("stroke-dasharray", null).attr("filter", null).attr("mask", null); - coordinates.attr("opacity", 1).attr("data-size", 12).attr("font-size", 12).attr("stroke", "#d4d4d4").attr("stroke-width", 1).attr("stroke-dasharray", 5).attr("filter", null).attr("mask", null); - compass.attr("opacity", 0.8).attr("transform", null).attr("filter", null).attr("mask", "url(#water)").attr("shape-rendering", "optimizespeed"); - if (!d3.select("#initial").size()) d3.select("#rose").attr("transform", "translate(80 80) scale(.25)"); + gridOverlay + .attr('opacity', 0.8) + .attr('type', 'pointyHex') + .attr('scale', 1) + .attr('dx', 0) + .attr('dy', 0) + .attr('stroke', '#777777') + .attr('stroke-width', 0.5) + .attr('stroke-dasharray', null) + .attr('filter', null) + .attr('mask', null); + coordinates.attr('opacity', 1).attr('data-size', 12).attr('font-size', 12).attr('stroke', '#d4d4d4').attr('stroke-width', 1).attr('stroke-dasharray', 5).attr('filter', null).attr('mask', null); + compass.attr('opacity', 0.8).attr('transform', null).attr('filter', null).attr('mask', 'url(#water)').attr('shape-rendering', 'optimizespeed'); + if (!d3.select('#initial').size()) d3.select('#rose').attr('transform', 'translate(80 80) scale(.25)'); - relig.attr("opacity", 0.7).attr("stroke", "#777777").attr("stroke-width", 0).attr("filter", null); - cults.attr("opacity", 0.6).attr("stroke", "#777777").attr("stroke-width", 0.5).attr("filter", null); - landmass.attr("opacity", 1).attr("fill", "#eef6fb").attr("filter", null); - markers.attr("opacity", null).attr("rescale", 1).attr("filter", "url(#dropShadow01)"); + relig.attr('opacity', 0.7).attr('stroke', '#777777').attr('stroke-width', 0).attr('filter', null); + cults.attr('opacity', 0.6).attr('stroke', '#777777').attr('stroke-width', 0.5).attr('filter', null); + landmass.attr('opacity', 1).attr('fill', '#eef6fb').attr('filter', null); + markers.attr('opacity', null).attr('rescale', 1).attr('filter', 'url(#dropShadow01)'); - prec.attr("opacity", null).attr("stroke", "#000000").attr("stroke-width", 0.1).attr("fill", "#003dff").attr("filter", null); - population.attr("opacity", null).attr("stroke-width", 1.6).attr("stroke-dasharray", null).attr("stroke-linecap", "butt").attr("filter", null); - population.select("#rural").attr("stroke", "#0000ff"); - population.select("#urban").attr("stroke", "#ff0000"); + prec.attr('opacity', null).attr('stroke', '#000000').attr('stroke-width', 0.1).attr('fill', '#003dff').attr('filter', null); + population.attr('opacity', null).attr('stroke-width', 1.6).attr('stroke-dasharray', null).attr('stroke-linecap', 'butt').attr('filter', null); + population.select('#rural').attr('stroke', '#0000ff'); + population.select('#urban').attr('stroke', '#ff0000'); - lakes.select("#freshwater").attr("opacity", 0.5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", 0.7).attr("filter", null); - lakes.select("#salt").attr("opacity", 0.5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", 0.7).attr("filter", null); - lakes.select("#sinkhole").attr("opacity", 1).attr("fill", "#5bc9fd").attr("stroke", "#53a3b0").attr("stroke-width", 0.7).attr("filter", null); - lakes.select("#frozen").attr("opacity", 0.95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null); - lakes.select("#lava").attr("opacity", 0.7).attr("fill", "#90270d").attr("stroke", "#f93e0c").attr("stroke-width", 2).attr("filter", "url(#crumpled)"); - lakes.select("#dry").attr("opacity", 1).attr("fill", "#c9bfa7").attr("stroke", "#8e816f").attr("stroke-width", 0.7).attr("filter", null); + lakes.select('#freshwater').attr('opacity', 0.5).attr('fill', '#a6c1fd').attr('stroke', '#5f799d').attr('stroke-width', 0.7).attr('filter', null); + lakes.select('#salt').attr('opacity', 0.5).attr('fill', '#409b8a').attr('stroke', '#388985').attr('stroke-width', 0.7).attr('filter', null); + lakes.select('#sinkhole').attr('opacity', 1).attr('fill', '#5bc9fd').attr('stroke', '#53a3b0').attr('stroke-width', 0.7).attr('filter', null); + lakes.select('#frozen').attr('opacity', 0.95).attr('fill', '#cdd4e7').attr('stroke', '#cfe0eb').attr('stroke-width', 0).attr('filter', null); + lakes.select('#lava').attr('opacity', 0.7).attr('fill', '#90270d').attr('stroke', '#f93e0c').attr('stroke-width', 2).attr('filter', 'url(#crumpled)'); + lakes.select('#dry').attr('opacity', 1).attr('fill', '#c9bfa7').attr('stroke', '#8e816f').attr('stroke-width', 0.7).attr('filter', null); - coastline.select("#sea_island").attr("opacity", 0.5).attr("stroke", "#1f3846").attr("stroke-width", 0.7).attr("auto-filter", 1).attr("filter", "url(#dropShadow)"); - coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", 0.35).attr("filter", null); + coastline.select('#sea_island').attr('opacity', 0.5).attr('stroke', '#1f3846').attr('stroke-width', 0.7).attr('auto-filter', 1).attr('filter', 'url(#dropShadow)'); + coastline.select('#lake_island').attr('opacity', 1).attr('stroke', '#7c8eaf').attr('stroke-width', 0.35).attr('filter', null); - terrain.attr("opacity", null).attr("set", "simple").attr("size", 1).attr("density", 0.4).attr("filter", null).attr("mask", null); - rivers.attr("opacity", null).attr("fill", "#5d97bb").attr("filter", null); - ruler.attr("opacity", null).attr("filter", null); + terrain.attr('opacity', null).attr('set', 'simple').attr('size', 1).attr('density', 0.4).attr('filter', null).attr('mask', null); + rivers.attr('opacity', null).attr('fill', '#5d97bb').attr('filter', null); + ruler.attr('opacity', null).attr('filter', null); - roads.attr("opacity", 0.9).attr("stroke", "#d06324").attr("stroke-width", 0.7).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt").attr("filter", null).attr("mask", null); - trails.attr("opacity", 0.9).attr("stroke", "#d06324").attr("stroke-width", 0.25).attr("stroke-dasharray", ".8 1.6").attr("stroke-linecap", "butt").attr("filter", null).attr("mask", null); - searoutes.attr("opacity", 0.8).attr("stroke", "#ffffff").attr("stroke-width", 0.45).attr("stroke-dasharray", "1 2").attr("stroke-linecap", "round").attr("filter", null).attr("mask", null); + roads.attr('opacity', 0.9).attr('stroke', '#d06324').attr('stroke-width', 0.7).attr('stroke-dasharray', '2').attr('stroke-linecap', 'butt').attr('filter', null).attr('mask', null); + trails.attr('opacity', 0.9).attr('stroke', '#d06324').attr('stroke-width', 0.25).attr('stroke-dasharray', '.8 1.6').attr('stroke-linecap', 'butt').attr('filter', null).attr('mask', null); + searoutes.attr('opacity', 0.8).attr('stroke', '#ffffff').attr('stroke-width', 0.45).attr('stroke-dasharray', '1 2').attr('stroke-linecap', 'round').attr('filter', null).attr('mask', null); - regions.attr("opacity", 0.4).attr("filter", null); - statesHalo.attr("data-width", 10).attr("stroke-width", 10).attr("opacity", 1); - provs.attr("opacity", 0.7).attr("fill", "#000000").attr("font-family", "Georgia").attr("data-font", "Georgia").attr("data-size", 10).attr("font-size", 10).attr("filter", null); + regions.attr('opacity', 0.4).attr('filter', null); + statesHalo.attr('data-width', 10).attr('stroke-width', 10).attr('opacity', 1); + provs.attr('opacity', 0.7).attr('fill', '#000000').attr('font-family', 'Georgia').attr('data-font', 'Georgia').attr('data-size', 10).attr('font-size', 10).attr('filter', null); - temperature.attr("opacity", null).attr("fill", "#000000").attr("stroke-width", 1.8).attr("fill-opacity", 0.3).attr("font-size", "8px").attr("stroke-dasharray", null).attr("filter", null).attr("mask", null); - texture.attr("opacity", null).attr("filter", null).attr("mask", "url(#land)"); - texture.select("#textureImage").attr("x", 0).attr("y", 0); - zones.attr("opacity", 0.6).attr("stroke", "#333333").attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt").attr("filter", null).attr("mask", null); + temperature + .attr('opacity', null) + .attr('fill', '#000000') + .attr('stroke-width', 1.8) + .attr('fill-opacity', 0.3) + .attr('font-size', '8px') + .attr('stroke-dasharray', null) + .attr('filter', null) + .attr('mask', null); + texture.attr('opacity', null).attr('filter', null).attr('mask', 'url(#land)'); + texture.select('#textureImage').attr('x', 0).attr('y', 0); + zones.attr('opacity', 0.6).attr('stroke', '#333333').attr('stroke-width', 0).attr('stroke-dasharray', null).attr('stroke-linecap', 'butt').attr('filter', null).attr('mask', null); // ocean and svg default style - svg.attr("background-color", "#000000").attr("data-filter", null).attr("filter", null); - oceanLayers.select("rect").attr("fill", "#466eab"); // old color #53679f - oceanLayers.attr("filter", null).attr("layers", "-6,-3,-1"); - svg.select("#oceanicPattern").attr("href", "./images/pattern1.png").attr("opacity", 0.2); + svg.attr('background-color', '#000000').attr('data-filter', null).attr('filter', null); + oceanLayers.select('rect').attr('fill', '#466eab'); // old color #53679f + oceanLayers.attr('filter', null).attr('layers', '-6,-3,-1'); + svg.select('#oceanicPattern').attr('href', './images/pattern1.png').attr('opacity', 0.2); // heightmap style - terrs.attr("opacity", null).attr("filter", null).attr("mask", "url(#land)").attr("stroke", "none").attr("scheme", "bright").attr("terracing", 0).attr("skip", 5).attr("relax", 0).attr("curve", 0); + terrs.attr('opacity', null).attr('filter', null).attr('mask', 'url(#land)').attr('stroke', 'none').attr('scheme', 'bright').attr('terracing', 0).attr('skip', 5).attr('relax', 0).attr('curve', 0); // legend - legend.attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 13).attr("data-size", 13).attr("data-x", 99).attr("data-y", 93).attr("data-columns", 8).attr("stroke-width", 2.5).attr("stroke", "#812929").attr("stroke-dasharray", "0 4 10 4").attr("stroke-linecap", "round"); - legend.select("#legendBox").attr("fill", "#ffffff").attr("fill-opacity", 0.8); + legend + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', 13) + .attr('data-size', 13) + .attr('data-x', 99) + .attr('data-y', 93) + .attr('data-columns', 8) + .attr('stroke-width', 2.5) + .attr('stroke', '#812929') + .attr('stroke-dasharray', '0 4 10 4') + .attr('stroke-linecap', 'round'); + legend.select('#legendBox').attr('fill', '#ffffff').attr('fill-opacity', 0.8); const citiesSize = Math.max(rn(8 - regionsInput.value / 20), 3); - burgLabels.select("#cities").attr("fill", "#3e3e4b").attr("opacity", 1).style("text-shadow", "white 0 0 4px").attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", citiesSize).attr("data-size", citiesSize); - burgIcons.select("#cities").attr("opacity", 1).attr("size", 1).attr("stroke-width", 0.24).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("fill-opacity", 0.7).attr("stroke-dasharray", "").attr("stroke-linecap", "butt"); - anchors.select("#cities").attr("opacity", 1).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("stroke-width", 1.2).attr("size", 2); + burgLabels + .select('#cities') + .attr('fill', '#3e3e4b') + .attr('opacity', 1) + .style('text-shadow', 'white 0 0 4px') + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', citiesSize) + .attr('data-size', citiesSize); + burgIcons + .select('#cities') + .attr('opacity', 1) + .attr('size', 1) + .attr('stroke-width', 0.24) + .attr('fill', '#ffffff') + .attr('stroke', '#3e3e4b') + .attr('fill-opacity', 0.7) + .attr('stroke-dasharray', '') + .attr('stroke-linecap', 'butt'); + anchors.select('#cities').attr('opacity', 1).attr('fill', '#ffffff').attr('stroke', '#3e3e4b').attr('stroke-width', 1.2).attr('size', 2); - burgLabels.select("#towns").attr("fill", "#3e3e4b").attr("opacity", 1).style("text-shadow", "white 0 0 4px").attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 3).attr("data-size", 4); - burgIcons.select("#towns").attr("opacity", 1).attr("size", 0.5).attr("stroke-width", 0.12).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("fill-opacity", 0.7).attr("stroke-dasharray", "").attr("stroke-linecap", "butt"); - anchors.select("#towns").attr("opacity", 1).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("stroke-width", 1.2).attr("size", 1); + burgLabels + .select('#towns') + .attr('fill', '#3e3e4b') + .attr('opacity', 1) + .style('text-shadow', 'white 0 0 4px') + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', 3) + .attr('data-size', 4); + burgIcons + .select('#towns') + .attr('opacity', 1) + .attr('size', 0.5) + .attr('stroke-width', 0.12) + .attr('fill', '#ffffff') + .attr('stroke', '#3e3e4b') + .attr('fill-opacity', 0.7) + .attr('stroke-dasharray', '') + .attr('stroke-linecap', 'butt'); + anchors.select('#towns').attr('opacity', 1).attr('fill', '#ffffff').attr('stroke', '#3e3e4b').attr('stroke-width', 1.2).attr('size', 1); const stateLabelSize = Math.max(rn(24 - regionsInput.value / 6), 6); - labels.select("#states").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).style("text-shadow", "white 0 0 4px").attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", stateLabelSize).attr("data-size", stateLabelSize).attr("filter", null); - labels.select("#addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).style("text-shadow", "white 0 0 4px").attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null); + labels + .select('#states') + .attr('fill', '#3e3e4b') + .attr('opacity', 1) + .attr('stroke', '#3a3a3a') + .attr('stroke-width', 0) + .style('text-shadow', 'white 0 0 4px') + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', stateLabelSize) + .attr('data-size', stateLabelSize) + .attr('filter', null); + labels + .select('#addedLabels') + .attr('fill', '#3e3e4b') + .attr('opacity', 1) + .attr('stroke', '#3a3a3a') + .attr('stroke-width', 0) + .style('text-shadow', 'white 0 0 4px') + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', 18) + .attr('data-size', 18) + .attr('filter', null); - fogging.attr("opacity", 0.98).attr("fill", "#30426f"); - emblems.attr("opacity", 0.9).attr("stroke-width", 1).attr("filter", null); + fogging.attr('opacity', 0.98).attr('fill', '#30426f'); + emblems.attr('opacity', 0.9).attr('stroke-width', 1).attr('filter', null); + + goods.attr('opacity', 1).attr('fill', '#000').attr('stroke', '#000').attr('stroke-width', 0.32).attr('filter', 'url(#dropShadow01)'); } // apply style settings in JSON @@ -837,12 +924,12 @@ function applyStyle(style) { for (const attribute in style[selector]) { const value = style[selector][attribute]; - if (value === "null" || value === null) { + if (value === 'null' || value === null) { el.removeAttribute(attribute); continue; } - if (attribute === "text-shadow") { + if (attribute === 'text-shadow') { el.style[attribute] = value; } else { el.setAttribute(attribute, value); @@ -853,38 +940,16 @@ function applyStyle(style) { // change current style preset to another saved one function changeStylePreset(preset) { - if (customization) { - tip("Please exit the customization mode first", false, "error"); - return; - } - alertMessage.innerHTML = "Are you sure you want to change the style preset? All unsaved style changes will be lost"; - $("#alert").dialog({ - resizable: false, - title: "Change style preset", - width: "23em", - buttons: { - Change: function () { - const customPreset = localStorage.getItem(preset); - if (customPreset) { - if (JSON.isValid(customPreset)) applyStyle(JSON.parse(customPreset)); - else { - tip("Cannot parse stored style JSON. Default style applied", false, "error", 5000); - applyDefaultStyle(); - } - } else if (defaultStyles[preset]) applyStyle(JSON.parse(defaultStyles[preset])); - else applyDefaultStyle(); + if (customization) return tip('Please exit the customization mode first', false, 'error'); - removeStyleButton.style.display = stylePreset.selectedOptions[0].dataset.system ? "none" : "inline-block"; - updateElements(); // change elements - selectStyleElement(); // re-select element to trigger values update - updateMapFilter(); - localStorage.setItem("presetStyle", preset); // save preset to use it onload - stylePreset.dataset.old = stylePreset.value; // save current value - $(this).dialog("close"); - }, - Cancel: function () { - stylePreset.value = stylePreset.dataset.old; - $(this).dialog("close"); + const message = 'Are you sure you want to change the style preset?
All unsaved style changes will be lost'; + const onConfirm = () => { + const customPreset = localStorage.getItem(preset); + if (customPreset) { + if (JSON.isValid(customPreset)) applyStyle(JSON.parse(customPreset)); + else { + tip('Cannot parse stored style JSON. Default style applied', false, 'error', 5000); + applyDefaultStyle(); } } else if (defaultStyles[preset]) applyStyle(JSON.parse(defaultStyles[preset])); else applyDefaultStyle(); @@ -901,34 +966,34 @@ function changeStylePreset(preset) { function updateElements() { // burgIcons to desired size - burgIcons.selectAll("g").each(function (d) { - const size = +this.getAttribute("size"); + burgIcons.selectAll('g').each(function (d) { + const size = +this.getAttribute('size'); d3.select(this) - .selectAll("circle") + .selectAll('circle') .each(function () { - this.setAttribute("r", size); + this.setAttribute('r', size); }); burgLabels - .select("g#" + this.id) - .selectAll("text") + .select('g#' + this.id) + .selectAll('text') .each(function () { - this.setAttribute("dy", `${size * -1.5}px`); + this.setAttribute('dy', `${size * -1.5}px`); }); }); // anchor icons to desired size - anchors.selectAll("g").each(function (d) { - const size = +this.getAttribute("size"); + anchors.selectAll('g').each(function (d) { + const size = +this.getAttribute('size'); d3.select(this) - .selectAll("use") + .selectAll('use') .each(function () { const id = +this.dataset.id; const x = pack.burgs[id].x, y = pack.burgs[id].y; - this.setAttribute("x", rn(x - size * 0.47, 2)); - this.setAttribute("y", rn(y - size * 0.47, 2)); - this.setAttribute("width", size); - this.setAttribute("height", size); + this.setAttribute('x', rn(x - size * 0.47, 2)); + this.setAttribute('y', rn(y - size * 0.47, 2)); + this.setAttribute('width', size); + this.setAttribute('height', size); }); }); @@ -941,10 +1006,10 @@ function updateElements() { } function addStylePreset() { - $("#styleSaver").dialog({ - title: "Style Saver", - width: "26em", - position: {my: "center", at: "center", of: "svg"} + $('#styleSaver').dialog({ + title: 'Style Saver', + width: '26em', + position: {my: 'center', at: 'center', of: 'svg'} }); styleSaverJSON.value = JSON.stringify(getStyle(), null, 2); @@ -954,82 +1019,82 @@ function addStylePreset() { modules.saveStyle = true; // add listeners - document.getElementById("styleSaverName").addEventListener("input", checkName); - document.getElementById("styleSaverSave").addEventListener("click", saveStyle); - document.getElementById("styleSaverDownload").addEventListener("click", styleDownload); - document.getElementById("styleSaverLoad").addEventListener("click", () => styleToLoad.click()); - document.getElementById("styleToLoad").addEventListener("change", function () { + document.getElementById('styleSaverName').addEventListener('input', checkName); + document.getElementById('styleSaverSave').addEventListener('click', saveStyle); + document.getElementById('styleSaverDownload').addEventListener('click', styleDownload); + document.getElementById('styleSaverLoad').addEventListener('click', () => styleToLoad.click()); + document.getElementById('styleToLoad').addEventListener('change', function () { uploadFile(this, styleUpload); }); function getStyle() { const style = {}, attributes = { - "#map": ["background-color", "filter", "data-filter"], - "#armies": ["font-size", "box-size", "stroke", "stroke-width", "fill-opacity", "filter"], - "#biomes": ["opacity", "filter", "mask"], - "#stateBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"], - "#provinceBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"], - "#cells": ["opacity", "stroke", "stroke-width", "filter", "mask"], - "#gridOverlay": ["opacity", "scale", "dx", "dy", "type", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "transform", "filter", "mask"], - "#coordinates": ["opacity", "data-size", "font-size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"], - "#compass": ["opacity", "transform", "filter", "mask", "shape-rendering"], - "#rose": ["transform"], - "#relig": ["opacity", "stroke", "stroke-width", "filter"], - "#cults": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"], - "#landmass": ["opacity", "fill", "filter"], - "#markers": ["opacity", "rescale", "filter"], - "#prec": ["opacity", "stroke", "stroke-width", "fill", "filter"], - "#population": ["opacity", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"], - "#rural": ["stroke"], - "#urban": ["stroke"], - "#freshwater": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#salt": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#sinkhole": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#frozen": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#lava": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#dry": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#sea_island": ["opacity", "stroke", "stroke-width", "filter", "auto-filter"], - "#lake_island": ["opacity", "stroke", "stroke-width", "filter"], - "#terrain": ["opacity", "set", "size", "density", "filter", "mask"], - "#rivers": ["opacity", "filter", "fill"], - "#ruler": ["opacity", "filter"], - "#roads": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"], - "#trails": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"], - "#searoutes": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"], - "#regions": ["opacity", "filter"], - "#statesHalo": ["opacity", "data-width", "stroke-width"], - "#provs": ["opacity", "fill", "font-size", "data-font", "font-family", "filter"], - "#temperature": ["opacity", "font-size", "fill", "fill-opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"], - "#ice": ["opacity", "fill", "stroke", "stroke-width", "filter"], - "#emblems": ["opacity", "stroke-width", "filter"], - "#texture": ["opacity", "filter", "mask"], - "#textureImage": ["x", "y"], - "#zones": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"], - "#oceanLayers": ["filter", "layers"], - "#oceanBase": ["fill"], - "#oceanicPattern": ["href", "opacity"], - "#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"], - "#legend": ["data-size", "font-size", "data-font", "font-family", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "data-x", "data-y", "data-columns"], - "#legendBox": ["fill", "fill-opacity"], - "#burgLabels > #cities": ["opacity", "fill", "text-shadow", "data-size", "font-size", "data-font", "font-family"], - "#burgIcons > #cities": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"], - "#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"], - "#burgLabels > #towns": ["opacity", "fill", "text-shadow", "data-size", "font-size", "data-font", "font-family"], - "#burgIcons > #towns": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"], - "#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"], - "#labels > #states": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "data-font", "font-family", "filter"], - "#labels > #addedLabels": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "data-font", "font-family", "filter"], - "#fogging": ["opacity", "fill", "filter"] + '#map': ['background-color', 'filter', 'data-filter'], + '#armies': ['font-size', 'box-size', 'stroke', 'stroke-width', 'fill-opacity', 'filter'], + '#biomes': ['opacity', 'filter', 'mask'], + '#stateBorders': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter'], + '#provinceBorders': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter'], + '#cells': ['opacity', 'stroke', 'stroke-width', 'filter', 'mask'], + '#gridOverlay': ['opacity', 'scale', 'dx', 'dy', 'type', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'transform', 'filter', 'mask'], + '#coordinates': ['opacity', 'data-size', 'font-size', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter', 'mask'], + '#compass': ['opacity', 'transform', 'filter', 'mask', 'shape-rendering'], + '#rose': ['transform'], + '#relig': ['opacity', 'stroke', 'stroke-width', 'filter'], + '#cults': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter'], + '#landmass': ['opacity', 'fill', 'filter'], + '#markers': ['opacity', 'rescale', 'filter'], + '#prec': ['opacity', 'stroke', 'stroke-width', 'fill', 'filter'], + '#population': ['opacity', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter'], + '#rural': ['stroke'], + '#urban': ['stroke'], + '#freshwater': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#salt': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#sinkhole': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#frozen': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#lava': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#dry': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#sea_island': ['opacity', 'stroke', 'stroke-width', 'filter', 'auto-filter'], + '#lake_island': ['opacity', 'stroke', 'stroke-width', 'filter'], + '#terrain': ['opacity', 'set', 'size', 'density', 'filter', 'mask'], + '#rivers': ['opacity', 'filter', 'fill'], + '#ruler': ['opacity', 'filter'], + '#roads': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter', 'mask'], + '#trails': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter', 'mask'], + '#searoutes': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter', 'mask'], + '#regions': ['opacity', 'filter'], + '#statesHalo': ['opacity', 'data-width', 'stroke-width'], + '#provs': ['opacity', 'fill', 'font-size', 'data-font', 'font-family', 'filter'], + '#temperature': ['opacity', 'font-size', 'fill', 'fill-opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter'], + '#ice': ['opacity', 'fill', 'stroke', 'stroke-width', 'filter'], + '#emblems': ['opacity', 'stroke-width', 'filter'], + '#texture': ['opacity', 'filter', 'mask'], + '#textureImage': ['x', 'y'], + '#zones': ['opacity', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'filter', 'mask'], + '#oceanLayers': ['filter', 'layers'], + '#oceanBase': ['fill'], + '#oceanicPattern': ['href', 'opacity'], + '#terrs': ['opacity', 'scheme', 'terracing', 'skip', 'relax', 'curve', 'filter', 'mask'], + '#legend': ['data-size', 'font-size', 'data-font', 'font-family', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap', 'data-x', 'data-y', 'data-columns'], + '#legendBox': ['fill', 'fill-opacity'], + '#burgLabels > #cities': ['opacity', 'fill', 'text-shadow', 'data-size', 'font-size', 'data-font', 'font-family'], + '#burgIcons > #cities': ['opacity', 'fill', 'fill-opacity', 'size', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap'], + '#anchors > #cities': ['opacity', 'fill', 'size', 'stroke', 'stroke-width'], + '#burgLabels > #towns': ['opacity', 'fill', 'text-shadow', 'data-size', 'font-size', 'data-font', 'font-family'], + '#burgIcons > #towns': ['opacity', 'fill', 'fill-opacity', 'size', 'stroke', 'stroke-width', 'stroke-dasharray', 'stroke-linecap'], + '#anchors > #towns': ['opacity', 'fill', 'size', 'stroke', 'stroke-width'], + '#labels > #states': ['opacity', 'fill', 'stroke', 'stroke-width', 'text-shadow', 'data-size', 'font-size', 'data-font', 'font-family', 'filter'], + '#labels > #addedLabels': ['opacity', 'fill', 'stroke', 'stroke-width', 'text-shadow', 'data-size', 'font-size', 'data-font', 'font-family', 'filter'], + '#fogging': ['opacity', 'fill', 'filter'] }; for (const selector in attributes) { const s = (style[selector] = {}); - attributes[selector].forEach(attr => { + attributes[selector].forEach((attr) => { const el = document.querySelector(selector); if (!el) return; let value = el.getAttribute(attr) || el.style[attr]; - if (attr === "font-size" && el.hasAttribute("data-size")) value = el.getAttribute("data-size"); + if (attr === 'font-size' && el.hasAttribute('data-size')) value = el.getAttribute('data-size'); s[attr] = parseValue(value); }); } @@ -1045,9 +1110,9 @@ function addStylePreset() { } function checkName() { - let tip = ""; - const v = "style" + styleSaverName.value; - const listed = Array.from(stylePreset.options).some(o => o.value == v); + let tip = ''; + const v = 'style' + styleSaverName.value; + const listed = Array.from(stylePreset.options).some((o) => o.value == v); const stored = localStorage.getItem(v); if (!stored && listed) tip = 'default'; else if (stored) tip = 'existing'; @@ -1057,22 +1122,22 @@ function addStylePreset() { function saveStyle() { if (!styleSaverJSON.value) { - tip("Please provide a style JSON", false, "error"); + tip('Please provide a style JSON', false, 'error'); return; } if (!JSON.isValid(styleSaverJSON.value)) { - tip("JSON string is not valid, please check the format", false, "error"); + tip('JSON string is not valid, please check the format', false, 'error'); return; } if (!styleSaverName.value) { - tip("Please provide a preset name", false, "error"); + tip('Please provide a preset name', false, 'error'); return; } - if (styleSaverTip.innerHTML === "default") { - tip("You cannot overwrite default preset, please change the name", false, "error"); + if (styleSaverTip.innerHTML === 'default') { + tip('You cannot overwrite default preset, please change the name', false, 'error'); return; } - const preset = "style" + styleSaverName.value; + const preset = 'style' + styleSaverName.value; applyOption(stylePreset, preset, styleSaverName.value); // add option localStorage.setItem('presetStyle', preset); // mark preset as default localStorage.setItem(preset, styleSaverJSON.value); // save preset @@ -1083,28 +1148,28 @@ function addStylePreset() { function styleDownload() { if (!styleSaverJSON.value) { - tip("Please provide a style JSON", false, "error"); + tip('Please provide a style JSON', false, 'error'); return; } if (!JSON.isValid(styleSaverJSON.value)) { - tip("JSON string is not valid, please check the format", false, "error"); + tip('JSON string is not valid, please check the format', false, 'error'); return; } if (!styleSaverName.value) { - tip("Please provide a preset name", false, "error"); + tip('Please provide a preset name', false, 'error'); return; } const data = styleSaverJSON.value; if (!data) { - tip("Please provide a style JSON", false, "error"); + tip('Please provide a style JSON', false, 'error'); return; } - downloadFile(data, "style" + styleSaverName.value + ".json", "application/json"); + downloadFile(data, 'style' + styleSaverName.value + '.json', 'application/json'); } function styleUpload(dataLoaded) { if (!dataLoaded) { - tip("Cannot load the file. Please check the data format", false, "error"); + tip('Cannot load the file. Please check the data format', false, 'error'); return; } const data = JSON.stringify(JSON.parse(dataLoaded), null, 2); @@ -1114,10 +1179,10 @@ function addStylePreset() { function removeStylePreset() { if (stylePreset.selectedOptions[0].dataset.system) { - tip("Cannot remove system preset", false, "error"); + tip('Cannot remove system preset', false, 'error'); return; } - localStorage.removeItem("presetStyle"); + localStorage.removeItem('presetStyle'); localStorage.removeItem(stylePreset.value); stylePreset.selectedOptions[0].remove(); removeStyleButton.style.display = 'none'; @@ -1128,29 +1193,52 @@ mapFilters.addEventListener('click', applyMapFilter); function applyMapFilter(event) { if (event.target.tagName !== 'BUTTON') return; const button = event.target; - svg.attr("data-filter", null).attr("filter", null); - if (button.classList.contains("pressed")) { - button.classList.remove("pressed"); + svg.attr('data-filter', null).attr('filter', null); + if (button.classList.contains('pressed')) { + button.classList.remove('pressed'); return; } - mapFilters.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); - button.classList.add("pressed"); - svg.attr("data-filter", button.id).attr("filter", "url(#filter-" + button.id + ")"); + mapFilters.querySelectorAll('.pressed').forEach((button) => button.classList.remove('pressed')); + button.classList.add('pressed'); + svg.attr('data-filter', button.id).attr('filter', 'url(#filter-' + button.id + ')'); } function updateMapFilter() { const filter = svg.attr('data-filter'); mapFilters.querySelectorAll('.pressed').forEach((button) => button.classList.remove('pressed')); if (!filter) return; - mapFilters.querySelector("#" + filter).classList.add("pressed"); + mapFilters.querySelector('#' + filter).classList.add('pressed'); } // FONTS // fetch default fonts if not done before function loadDefaultFonts() { if (!$('link[href="fonts.css"]').length) { - $("head").append(''); - const fontsToAdd = ["Amatic+SC:700", "IM+Fell+English", "Great+Vibes", "MedievalSharp", "Metamorphous", "Nova+Script", "Uncial+Antiqua", "Underdog", "Caesar+Dressing", "Bitter", "Yellowtail", "Montez", "Shadows+Into+Light", "Fredericka+the+Great", "Orbitron", "Dancing+Script:700", "Architects+Daughter", "Kaushan+Script", "Gloria+Hallelujah", "Satisfy", "Comfortaa:700", "Cinzel"]; + $('head').append(''); + const fontsToAdd = [ + 'Amatic+SC:700', + 'IM+Fell+English', + 'Great+Vibes', + 'MedievalSharp', + 'Metamorphous', + 'Nova+Script', + 'Uncial+Antiqua', + 'Underdog', + 'Caesar+Dressing', + 'Bitter', + 'Yellowtail', + 'Montez', + 'Shadows+Into+Light', + 'Fredericka+the+Great', + 'Orbitron', + 'Dancing+Script:700', + 'Architects+Daughter', + 'Kaushan+Script', + 'Gloria+Hallelujah', + 'Satisfy', + 'Comfortaa:700', + 'Cinzel' + ]; fontsToAdd.forEach(function (f) { if (fonts.indexOf(f) === -1) fonts.push(f); }); @@ -1191,17 +1279,17 @@ function fetchFonts(url) { function addFonts(url) { $('head').append(''); return fetch(url) - .then(resp => resp.text()) - .then(text => { - let s = document.createElement("style"); + .then((resp) => resp.text()) + .then((text) => { + let s = document.createElement('style'); s.innerHTML = text; document.head.appendChild(s); - let styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0]; - let FontRule = rule => { - let family = rule.style.getPropertyValue("font-family"); - let font = family.replace(/['"]+/g, "").replace(/ /g, "+"); - let weight = rule.style.getPropertyValue("font-weight"); - if (weight && weight !== "400") font += ":" + weight; + let styleSheet = Array.prototype.filter.call(document.styleSheets, (sS) => sS.ownerNode === s)[0]; + let FontRule = (rule) => { + let family = rule.style.getPropertyValue('font-family'); + let font = family.replace(/['"]+/g, '').replace(/ /g, '+'); + let weight = rule.style.getPropertyValue('font-weight'); + if (weight && weight !== '400') font += ':' + weight; if (fonts.indexOf(font) == -1) { fonts.push(font); fetched++; @@ -1219,11 +1307,11 @@ function addFonts(url) { // Update font list for Label and Burg Editors function updateFontOptions() { - styleSelectFont.innerHTML = ""; + styleSelectFont.innerHTML = ''; for (let i = 0; i < fonts.length; i++) { - const opt = document.createElement("option"); + const opt = document.createElement('option'); opt.value = i; - const font = fonts[i].split(":")[0].replace(/\+/g, " "); + const font = fonts[i].split(':')[0].replace(/\+/g, ' '); opt.style.fontFamily = opt.innerHTML = font; styleSelectFont.add(opt); } diff --git a/modules/ui/tools.js b/modules/ui/tools.js index 5546921d..da3282f3 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -17,6 +17,7 @@ toolsContent.addEventListener('click', function (event) { else if (button === 'editDiplomacyButton') editDiplomacy(); else if (button === 'editCulturesButton') editCultures(); else if (button === 'editReligions') editReligions(); + else if (button === 'editResources') editResources(); else if (button === 'editEmblemButton') openEmblemEditor(); else if (button === 'editNamesBaseButton') editNamesbase(); else if (button === 'editUnitsButton') editUnits(); @@ -83,6 +84,7 @@ function processFeatureRegeneration(event, button) { else if (button === 'regenerateStates') regenerateStates(); else if (button === 'regenerateProvinces') regenerateProvinces(); else if (button === 'regenerateBurgs') regenerateBurgs(); + else if (button === 'regenerateResources') regenerateResources(); else if (button === 'regenerateEmblems') regenerateEmblems(); else if (button === 'regenerateReligions') regenerateReligions(); else if (button === 'regenerateCultures') regenerateCultures();