From 3359df0e029f1f04435fc85d9a755de8479dce02 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Sun, 16 May 2021 18:51:34 +0300 Subject: [PATCH] generic confirmationDialog for all files --- modules/save-and-load.js | 1294 +++++++++++++++-------------- modules/ui/burg-editor.js | 501 +++++++----- modules/ui/burgs-overview.js | 477 ++++++----- modules/ui/coastline-editor.js | 185 +++-- modules/ui/cultures-editor.js | 689 +++++++++------- modules/ui/heightmap-editor.js | 1246 +++++++++++++++------------- modules/ui/ice-editor.js | 117 +-- modules/ui/labels-editor.js | 351 ++++---- modules/ui/lakes-editor.js | 213 ++--- modules/ui/markers-editor.js | 320 ++++---- modules/ui/military-overview.js | 286 ++++--- modules/ui/namesbase-editor.js | 250 +++--- modules/ui/notes-editor.js | 106 ++- modules/ui/options.js | 712 +++++++++------- modules/ui/provinces-editor.js | 885 +++++++++++--------- modules/ui/regiment-editor.js | 389 +++++---- modules/ui/relief-editor.js | 241 +++--- modules/ui/religions-editor.js | 628 ++++++++------ modules/ui/resources-editor.js | 3 +- modules/ui/rivers-editor.js | 203 ++--- modules/ui/rivers-overview.js | 156 ++-- modules/ui/routes-editor.js | 293 +++---- modules/ui/states-editor.js | 894 +++++++++++--------- modules/ui/style.js | 1352 +++++++++++++++++-------------- modules/ui/units-editor.js | 390 +++++---- 25 files changed, 6727 insertions(+), 5454 deletions(-) diff --git a/modules/save-and-load.js b/modules/save-and-load.js index e82fa257..1d29590f 100644 --- a/modules/save-and-load.js +++ b/modules/save-and-load.js @@ -1,161 +1,164 @@ // 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(); img.src = url; - img.onload = function() { + img.onload = function () { ctx.drawImage(img, 0, 0, canvas.width, canvas.height); - 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); - }, 1000); + 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); + }, 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); - const quality = Math.min(rn(1 - pngResolutionInput.value / 20, 2), .92); - const URL = await canvas.toDataURL("image/jpeg", quality); - const link = document.createElement("a"); - link.download = getFileName() + ".jpeg"; + img.onload = async function () { + 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'; 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'); } // 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); - clone.select("#debug").remove(); + 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); + 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"); - for (let i=0; i < filters.length; i++) { + 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.querySelector("[filter='url(#" + id + ")']")) continue; + if (cloneEl.getAttribute('filter') === 'url(#' + id + ')') continue; filters[i].remove(); } // remove unused patterns - const patterns = cloneEl.querySelectorAll("pattern"); - for (let i=0; i < patterns.length; i++) { + 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; + if (cloneEl.querySelector("[fill='url(#" + id + ")']")) continue; patterns[i].remove(); } // remove unused symbols - const symbols = cloneEl.querySelectorAll("symbol"); - for (let i=0; i < symbols.length; i++) { + 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; + if (cloneEl.querySelector("use[*|href='#" + id + "']")) continue; symbols[i].remove(); } // add displayed emblems - if (layerIsOn("toggleEmblems") && emblems.selectAll("use").size()) { - cloneEl.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)); - }); + if (layerIsOn('toggleEmblems') && emblems.selectAll('use').size()) { + cloneEl + .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(); } // 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; - for (let i=0; i < terrainNodes.length; i++) { - const href = terrainNodes[i].getAttribute("href") || terrainNodes[i].getAttribute("xlink:href"); + 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'); 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)); @@ -163,37 +166,41 @@ 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', + '' + ); 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 serialized = `` + new XMLSerializer().serializeToString(cloneEl); 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); @@ -202,46 +209,55 @@ 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;) { + for (let empty = 1; empty; ) { empty = 0; - 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"); + 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'); }); } } 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").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)); + clone.select('#heights').attr('filter', 'url(#blur1)'); + clone + .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)); } // 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++) { + 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 + ';'; @@ -250,13 +266,13 @@ function inlineStyle(clone) { 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 + ';'; } - if (style != "") this.setAttribute('style', style); + if (style != '') this.setAttribute('style', style); }); emptyG.remove(); @@ -264,37 +280,37 @@ 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 => { + .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 FontRule = (rule) => { const src = rule.style.getPropertyValue('src'); - const url = src ? src.split('url(')[1].split(')')[0] : ""; + const url = src ? src.split('url(')[1].split(')')[0] : ''; return {rule, src, url: url.substring(url.length - 1, 1)}; - } + }; const fontProms = []; for (const r of styleSheet.cssRules) { @@ -303,16 +319,16 @@ function GFontToDataURI(url) { fontProms.push( fetch(fR.url) // fetch the actual font-file (.woff) - .then(resp => resp.blob()) - .then(blob => { - return new Promise(resolve => { - let f = new FileReader(); - f.onload = e => resolve(f.result); - f.readAsDataURL(blob); + .then((resp) => resp.blob()) + .then((blob) => { + return new Promise((resolve) => { + let f = new FileReader(); + 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 return Promise.all(fontProms); // wait for all this has been done @@ -321,39 +337,55 @@ 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, - barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value, - barPosX.value, barPosY.value, populationRate.value, urbanization.value, - mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value, - temperaturePoleOutput.value, precOutput.value, JSON.stringify(options), - mapName.value].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, + barSize.value, + barLabel.value, + barBackOpacity.value, + barBackColor.value, + barPosX.value, + barPosY.value, + populationRate.value, + urbanization.value, + mapSizeOutput.value, + latitudeOutput.value, + temperatureEquatorOutput.value, + temperaturePoleOutput.value, + precOutput.value, + JSON.stringify(options), + mapName.value + ].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); + const svg_xml = new XMLSerializer().serializeToString(cloneEl); - const gridGeneral = JSON.stringify({spacing:grid.spacing, cellsX:grid.cellsX, cellsY:grid.cellsY, boundary:grid.boundary, points:grid.points, features:grid.features}); + const gridGeneral = JSON.stringify({spacing: grid.spacing, cellsX: grid.cellsX, cellsY: grid.cellsY, boundary: grid.boundary, points: grid.points, features: grid.features}); const features = JSON.stringify(pack.features); const cultures = JSON.stringify(pack.cultures); const states = JSON.stringify(pack.states); @@ -364,51 +396,88 @@ function getMapData() { // 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; - return `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${names}`; - }).join("/"); + const namesData = nameBases + .map((b, i) => { + const names = defaultNB[i] && defaultNB[i].b === b.b ? '' : b.b; + return `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${names}`; + }) + .join('/'); // round population to save resources - const pop = Array.from(pack.cells.pop).map(p => rn(p, 4)); + 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 + ].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) {tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return;} - closeDialogs("#alert"); + if (customization) { + tip('Map cannot be saved when edit mode is active, please exit the mode and retry', false, 'error'); + return; + } + 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 [r, u] = getCellPopulation(i); return rn(r+u)}; - const getHeight = i => parseInt(getFriendlyHeight([cells.p[i][0],cells.p[i][1]])); + 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]])); - cells.i.forEach(i => { + cells.i.forEach((i) => { const coordinates = getCellCoordinates(cells.v[i]); const height = getHeight(i); const biome = cells.biome[i]; @@ -420,76 +489,76 @@ function saveGeoJSON_Cells() { const religion = cells.religion[i]; 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 properties = {id: i, height, biome, type, population, state, province, culture, religion, neighbors}; + 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 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]])]; } @@ -497,7 +566,7 @@ function getRoutePoints(node) { let points = []; const l = node.getTotalLength(); const increment = l / Math.ceil(l / 2); - for (let i=0; i <= l; i += increment) { + for (let i = 0; i <= l; i += increment) { const p = node.getPointAtLength(i); points.push(getQGIScoordinates(p.x, p.y)); } @@ -508,87 +577,85 @@ function getRiverPoints(node) { let points = []; const l = node.getTotalLength() / 2; // half-length const increment = 0.25; // defines density of points - for (let i=l, c=i; i >= 0; i -= increment, c += increment) { + for (let i = l, c = i; i >= 0; i -= increment, c += increment) { const p1 = node.getPointAtLength(i); const p2 = node.getPointAtLength(c); const [x, y] = getQGIScoordinates((p1.x + p2.x) / 2, (p1.y + p2.y) / 2); - points.push([x,y]); + points.push([x, y]); } return points; } async function quickSave() { - if (customization) {tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return;} + if (customization) { + 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); } 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'); } }); } function loadMapPrompt(blob) { const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes - if (workingTime < 5) {loadLastSavedMap(); return;} + if (workingTime < 5) return loadLastSavedMap(); - alertMessage.innerHTML = `Are you sure you want to load saved map?
- All unsaved changes made to the current map will be lost`; - $("#alert").dialog({resizable: false, title: "Load saved map", - buttons: { - Cancel: function() {$(this).dialog("close");}, - Load: function() {loadLastSavedMap(); $(this).dialog("close");} - } - }); + const message = 'Are you sure you want to load saved map?
All unsaved changes made to the current map will be lost'; + confirmationDialog({title: 'Load saved map', message, confirm: 'Load', onConfirm: loadLastSavedMap}); function loadLastSavedMap() { - WARN && console.warn("Load last saved map"); + WARN && console.warn('Load last saved map'); try { uploadMap(blob); - } - catch(error) { + } catch (error) { ERROR && console.error(error); - tip("Cannot load last saved map", true, "error", 2000); + tip('Cannot load last saved map', true, 'error', 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", +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", + '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", + '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"]; + "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; -} +}; 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(); } } @@ -597,19 +664,23 @@ function uploadMap(file, callback) { uploadMap.timeStart = performance.now(); const fileReader = new FileReader(); - fileReader.onload = function(fileLoadedEvent) { + 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]; - if (mapVersion === version) {parseLoadedData(data); return;} + 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 = "", load = false; + 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.
Please try to open it using an ${archive}`; @@ -618,16 +689,23 @@ function uploadMap(file, callback) {
Please keep using an ${archive}`; } else { load = true; - message = `The map version (${mapVersion}) does not match the Generator version (${version}). + message = `The map version (${mapVersion}) does not match the Generator version (${version}).
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", buttons: { - OK: function() {$(this).dialog("close"); if (load) parseLoadedData(data);} - }}); + $('#alert').dialog({ + title: 'Version conflict', + width: '38em', + buttons: { + OK: function () { + $(this).dialog('close'); + if (load) parseLoadedData(data); + } + } + }); }; - fileReader.readAsText(file, "UTF-8"); + fileReader.readAsText(file, 'UTF-8'); } function parseLoadedData(data) { @@ -637,21 +715,24 @@ 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("|"); - if (params[3]) {seed = params[3]; optionsSeed.value = seed;} + void (function parseParameters() { + const params = data[0].split('|'); + if (params[3]) { + seed = params[3]; + optionsSeed.value = seed; + } if (params[4]) graphWidth = +params[4]; if (params[5]) graphHeight = +params[5]; 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("|"); + void (function parseSettings() { + 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]; @@ -673,97 +754,97 @@ function parseLoadedData(data) { if (settings[18]) precInput.value = precOutput.value = settings[18]; if (settings[19]) options = JSON.parse(settings[19]); if (settings[20]) mapName.value = settings[20]; - }() + })(); - void function parseConfiguration() { + void (function parseConfiguration() { if (data[2]) mapCoordinates = JSON.parse(data[2]); 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++) { + for (let i = biomesData.i.length; i < biomesData.name.length; i++) { biomesData.i.push(biomesData.i.length); biomesData.iconsDensity.push(0); biomesData.icons.push([]); biomesData.cost.push(50); } - }() + })(); - void function replaceSVG() { + 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"); - goods = viewbox.select("#goods"); - 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 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'); + goods = viewbox.select('#goods'); + 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() { + 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() { + void (function parsePackData() { pack = {}; reGraph(); reMarkFeatures(); @@ -771,110 +852,121 @@ 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]) : []; 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); 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; - nameBases[i] = {name:e[0], min:e[1], max:e[2], d:e[3], m:e[4], 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() { + void (function restoreLayersState() { // turn all layers off - document.getElementById("mapLayers").querySelectorAll("li").forEach(el => el.classList.add("buttonoff")); + document + .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 (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"); + 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()); - }() + 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()); + })(); - void function resolveVersionConflicts() { - const version = parseFloat(data[0].split("|")[0]); + void (function resolveVersionConflicts() { + 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", .8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt"); - provinceBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .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", .6); + provs = viewbox.insert('g', '#borders').attr('id', 'provs').attr('opacity', 0.6); BurgsAndStates.collectStatistics(); BurgsAndStates.generateCampaigns(); BurgsAndStates.generateDiplomacy(); @@ -882,141 +974,149 @@ 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", .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()) {addMarkers(); turnButtonOn("toggleMarkers");} + if (!markers.selectAll('*').size()) { + addMarkers(); + 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 => { - r.origin = 0; - r.code = r.name.slice(0, 2); - }); + pack.religions + .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", .5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", .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", .5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", .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", .7).attr("filter", null); - lakes.select("#frozen").attr("opacity", .95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null); - lakes.select("#lava").attr("opacity", .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", .5).attr("stroke", "#1f3846").attr("stroke-width", .7).attr("filter", "url(#dropShadow)"); - coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", .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 => { - c.origin = 0; - c.code = c.name.slice(0, 2); - }); + pack.cultures + .filter((c) => c.i) + .forEach((c) => { + c.origin = 0; + c.code = c.name.slice(0, 2); + }); } // v 1.11 had an issue with fogging being displayed on load 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", .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), e = this.getPointAtLength(0); - const source = findCell(s.x, s.y), mouth = findCell(e.x, e.y); + const s = this.getPointAtLength(length), + e = this.getPointAtLength(0); + 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"; - pack.rivers.push({i, parent:0, length, source, mouth, basin:i, name, type}); + 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}); }); - } if (version < 1.22) { @@ -1028,8 +1128,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(.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}; @@ -1037,22 +1137,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", .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", .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 @@ -1062,69 +1162,69 @@ 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 = .1; + river.sourceWidth = 0.1; } else { Rivers.remove(river.i); } @@ -1132,14 +1232,14 @@ 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 + .006 * height) / 50 + 75) / (80 - f.temp); + const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp); f.evaporation = rn(evaporation * f.cells); f.name = f.name || Lakes.getName(f); delete f.river; @@ -1148,34 +1248,37 @@ 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], [x2, y2]]; + const points = [ + [x1, y1], + [x2, y2] + ]; 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; const step = length > 1000 ? 40 : length > 400 ? 20 : 10; const increment = length / Math.ceil(length / step); const points = []; - for (let i=0; i <= length; i += increment) { + for (let i = 0; i <= length; i += increment) { const point = this.getPointAtLength(i); points.push([point.x | 0, point.y | 0]); } @@ -1183,122 +1286,125 @@ 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 = ``; - document.getElementById("oceanPattern").setAttribute("opacity", .2); + document.getElementById('oceanPattern').setAttribute('opacity', 0.2); } - }() + })(); if (version < 1.62) { // v 1.62 changed grid data - gridOverlay.attr("size", null); + gridOverlay.attr('size', null); } if (version < 1.7) { // v 1.7 added resources layer - goods = viewbox.append("g").attr("id", "goods"); + goods = viewbox.append('g').attr('id', 'goods'); } - void function checkDataIntegrity() { + void (function checkDataIntegrity() { 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); b.port = 0;} + if (b.port < 0) { + 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 }); - }() + })(); 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; @@ -1308,25 +1414,35 @@ function parseLoadedData(data) { focusOn(); // based on searchParams focus on point, cell or burg invokeActiveZooming(); - WARN && console.warn(`TOTAL: ${rn((performance.now()-uploadMap.timeStart)/1000,2)}s`); + 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); - } - catch(error) { + INFO && console.groupEnd('Loaded Map ' + seed); + tip('Map is successfully loaded', true, 'success', 7000); + } catch (error) { ERROR && console.error(error); clearMainTip(); 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({ - resizable: false, title: "Loading error", maxWidth:"50em", buttons: { - "Select file": function() {$(this).dialog("close"); mapToLoad.click();}, - "New map": function() {$(this).dialog("close"); regenerateMap();}, - Cancel: function() {$(this).dialog("close")} - }, position: {my: "center", at: "center", of: "svg"} + $('#alert').dialog({ + resizable: false, + title: 'Loading error', + maxWidth: '50em', + buttons: { + 'Select file': function () { + $(this).dialog('close'); + mapToLoad.click(); + }, + 'New map': function () { + $(this).dialog('close'); + regenerateMap(); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); } - } diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js index 10d86583..19f9ccb8 100644 --- a/modules/ui/burg-editor.js +++ b/modules/ui/burg-editor.js @@ -1,200 +1,246 @@ -"use strict"; +'use strict'; function editBurg(id) { if (customization) return; - closeDialogs(".stable"); - if (!layerIsOn("toggleIcons")) toggleIcons(); - if (!layerIsOn("toggleLabels")) toggleLabels(); + closeDialogs('.stable'); + if (!layerIsOn('toggleIcons')) toggleIcons(); + if (!layerIsOn('toggleLabels')) toggleLabels(); const burg = id || d3.event.target.dataset.id; elSelected = burgLabels.select("[data-id='" + burg + "']"); - burgLabels.selectAll("text").call(d3.drag().on("start", dragBurgLabel)).classed("draggable", true); + burgLabels.selectAll('text').call(d3.drag().on('start', dragBurgLabel)).classed('draggable', true); updateBurgValues(); - const my = id || d3.event.target.tagName === "text" ? "center bottom-20" : "center top+20"; - const at = id ? "center" : d3.event.target.tagName === "text" ? "top" : "bottom"; - const of = id ? "svg" : d3.event.target; + const my = id || d3.event.target.tagName === 'text' ? 'center bottom-20' : 'center top+20'; + const at = id ? 'center' : d3.event.target.tagName === 'text' ? 'top' : 'bottom'; + const of = id ? 'svg' : d3.event.target; - $("#burgEditor").dialog({ - title: "Edit Burg", resizable: false, close: closeBurgEditor, - position: {my, at, of, collision: "fit"} + $('#burgEditor').dialog({ + title: 'Edit Burg', + resizable: false, + close: closeBurgEditor, + position: {my, at, of, collision: 'fit'} }); if (modules.editBurg) return; modules.editBurg = true; // add listeners - document.getElementById("burgGroupShow").addEventListener("click", showGroupSection); - document.getElementById("burgGroupHide").addEventListener("click", hideGroupSection); - document.getElementById("burgSelectGroup").addEventListener("change", changeGroup); - document.getElementById("burgInputGroup").addEventListener("change", createNewGroup); - document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput); - document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup); + document.getElementById('burgGroupShow').addEventListener('click', showGroupSection); + document.getElementById('burgGroupHide').addEventListener('click', hideGroupSection); + document.getElementById('burgSelectGroup').addEventListener('change', changeGroup); + document.getElementById('burgInputGroup').addEventListener('change', createNewGroup); + document.getElementById('burgAddGroup').addEventListener('click', toggleNewGroupInput); + document.getElementById('burgRemoveGroup').addEventListener('click', removeBurgsGroup); - document.getElementById("burgName").addEventListener("input", changeName); - document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom); - document.getElementById("burgType").addEventListener("input", changeType); - document.getElementById("burgCulture").addEventListener("input", changeCulture); - document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture); - document.getElementById("burgPopulation").addEventListener("change", changePopulation); - burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature)); + document.getElementById('burgName').addEventListener('input', changeName); + document.getElementById('burgNameReRandom').addEventListener('click', generateNameRandom); + document.getElementById('burgType').addEventListener('input', changeType); + document.getElementById('burgCulture').addEventListener('input', changeCulture); + document.getElementById('burgNameReCulture').addEventListener('click', generateNameCulture); + document.getElementById('burgPopulation').addEventListener('change', changePopulation); + burgBody.querySelectorAll('.burgFeature').forEach((el) => el.addEventListener('click', toggleFeature)); - document.getElementById("burgStyleShow").addEventListener("click", showStyleSection); - document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection); - document.getElementById("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle); - document.getElementById("burgEditIconStyle").addEventListener("click", editGroupIconStyle); - document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle); + document.getElementById('burgStyleShow').addEventListener('click', showStyleSection); + document.getElementById('burgStyleHide').addEventListener('click', hideStyleSection); + document.getElementById('burgEditLabelStyle').addEventListener('click', editGroupLabelStyle); + document.getElementById('burgEditIconStyle').addEventListener('click', editGroupIconStyle); + document.getElementById('burgEditAnchorStyle').addEventListener('click', editGroupAnchorStyle); - document.getElementById("burgSeeInMFCG").addEventListener("click", openInMFCG); - document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit); - document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg); - document.getElementById("burglLegend").addEventListener("click", editBurgLegend); - document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton); - document.getElementById("burgLock").addEventListener("mouseover", showBurgELockTip); - document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg); + document.getElementById('burgSeeInMFCG').addEventListener('click', openInMFCG); + document.getElementById('burgEditEmblem').addEventListener('click', openEmblemEdit); + document.getElementById('burgRelocate').addEventListener('click', toggleRelocateBurg); + document.getElementById('burglLegend').addEventListener('click', editBurgLegend); + document.getElementById('burgLock').addEventListener('click', toggleBurgLockButton); + document.getElementById('burgLock').addEventListener('mouseover', showBurgELockTip); + document.getElementById('burgRemove').addEventListener('click', removeSelectedBurg); function updateBurgValues() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); const b = pack.burgs[id]; const province = pack.cells.province[b.cell]; - const provinceName = province ? pack.provinces[province].fullName + ", " : ""; + const provinceName = province ? pack.provinces[province].fullName + ', ' : ''; const stateName = pack.states[b.state].fullName || pack.states[b.state].name; - document.getElementById("burgProvinceAndState").innerHTML = provinceName + stateName; + document.getElementById('burgProvinceAndState').innerHTML = provinceName + stateName; - document.getElementById("burgName").value = b.name; - document.getElementById("burgType").value = b.type || "Generic"; - document.getElementById("burgPopulation").value = rn(b.population * populationRate.value * urbanization.value); - document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none"; + document.getElementById('burgName').value = b.name; + document.getElementById('burgType').value = b.type || 'Generic'; + document.getElementById('burgPopulation').value = rn(b.population * populationRate.value * urbanization.value); + document.getElementById('burgEditAnchorStyle').style.display = +b.port ? 'inline-block' : 'none'; // update list and select culture - const cultureSelect = document.getElementById("burgCulture"); + const cultureSelect = document.getElementById('burgCulture'); cultureSelect.options.length = 0; - const cultures = pack.cultures.filter(c => !c.removed); - cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture))); + const cultures = pack.cultures.filter((c) => !c.removed); + cultures.forEach((c) => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture))); const temperature = grid.cells.temp[pack.cells.g[b.cell]]; - document.getElementById("burgTemperature").innerHTML = convertTemperature(temperature); - document.getElementById("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature); - document.getElementById("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]); + document.getElementById('burgTemperature').innerHTML = convertTemperature(temperature); + document.getElementById('burgTemperatureLikeIn').innerHTML = getTemperatureLikeness(temperature); + document.getElementById('burgElevation').innerHTML = getHeight(pack.cells.h[b.cell]); // toggle features - if (b.capital) document.getElementById("burgCapital").classList.remove("inactive"); - else document.getElementById("burgCapital").classList.add("inactive"); - if (b.port) document.getElementById("burgPort").classList.remove("inactive"); - else document.getElementById("burgPort").classList.add("inactive"); - if (b.citadel) document.getElementById("burgCitadel").classList.remove("inactive"); - else document.getElementById("burgCitadel").classList.add("inactive"); - if (b.walls) document.getElementById("burgWalls").classList.remove("inactive"); - else document.getElementById("burgWalls").classList.add("inactive"); - if (b.plaza) document.getElementById("burgPlaza").classList.remove("inactive"); - else document.getElementById("burgPlaza").classList.add("inactive"); - if (b.temple) document.getElementById("burgTemple").classList.remove("inactive"); - else document.getElementById("burgTemple").classList.add("inactive"); - if (b.shanty) document.getElementById("burgShanty").classList.remove("inactive"); - else document.getElementById("burgShanty").classList.add("inactive"); + if (b.capital) document.getElementById('burgCapital').classList.remove('inactive'); + else document.getElementById('burgCapital').classList.add('inactive'); + if (b.port) document.getElementById('burgPort').classList.remove('inactive'); + else document.getElementById('burgPort').classList.add('inactive'); + if (b.citadel) document.getElementById('burgCitadel').classList.remove('inactive'); + else document.getElementById('burgCitadel').classList.add('inactive'); + if (b.walls) document.getElementById('burgWalls').classList.remove('inactive'); + else document.getElementById('burgWalls').classList.add('inactive'); + if (b.plaza) document.getElementById('burgPlaza').classList.remove('inactive'); + else document.getElementById('burgPlaza').classList.add('inactive'); + if (b.temple) document.getElementById('burgTemple').classList.remove('inactive'); + else document.getElementById('burgTemple').classList.add('inactive'); + if (b.shanty) document.getElementById('burgShanty').classList.remove('inactive'); + else document.getElementById('burgShanty').classList.add('inactive'); //toggle lock updateBurgLockIcon(); // select group const group = elSelected.node().parentNode.id; - const select = document.getElementById("burgSelectGroup"); + const select = document.getElementById('burgSelectGroup'); select.options.length = 0; // remove all options - burgLabels.selectAll("g").each(function() { + burgLabels.selectAll('g').each(function () { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); // set emlem image - const coaID = "burgCOA"+id; + const coaID = 'burgCOA' + id; COArenderer.trigger(coaID, b.coa); - document.getElementById("burgEmblem").setAttribute("href", "#" + coaID); + document.getElementById('burgEmblem').setAttribute('href', '#' + coaID); } // in °C, array from -1 °C; source: https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature function getTemperatureLikeness(temperature) { - if (temperature < -5) return "Yakutsk"; + if (temperature < -5) return 'Yakutsk'; const cities = [ - "Snag (Yukon)", "Yellowknife (Canada)", "Okhotsk (Russia)", "Fairbanks (Alaska)", "Nuuk (Greenland)", "Murmansk", // -5 - 0 - "Arkhangelsk", "Anchorage", "Tromsø", "Reykjavik", "Riga", "Stockholm", "Halifax", "Prague", "Copenhagen", "London", // 1 - 10 - "Antwerp", "Paris", "Milan", "Batumi", "Rome", "Dubrovnik", "Lisbon", "Barcelona", "Marrakesh", "Alexandria", // 11 - 20 - "Tegucigalpa", "Guangzhou", "Rio de Janeiro", "Dakar", "Miami", "Jakarta", "Mogadishu", "Bangkok", "Aden", "Khartoum"]; // 21 - 30 - if (temperature > 30) return "Mecca"; - return cities[temperature+5] || null; + 'Snag (Yukon)', + 'Yellowknife (Canada)', + 'Okhotsk (Russia)', + 'Fairbanks (Alaska)', + 'Nuuk (Greenland)', + 'Murmansk', // -5 - 0 + 'Arkhangelsk', + 'Anchorage', + 'Tromsø', + 'Reykjavik', + 'Riga', + 'Stockholm', + 'Halifax', + 'Prague', + 'Copenhagen', + 'London', // 1 - 10 + 'Antwerp', + 'Paris', + 'Milan', + 'Batumi', + 'Rome', + 'Dubrovnik', + 'Lisbon', + 'Barcelona', + 'Marrakesh', + 'Alexandria', // 11 - 20 + 'Tegucigalpa', + 'Guangzhou', + 'Rio de Janeiro', + 'Dakar', + 'Miami', + 'Jakarta', + 'Mogadishu', + 'Bangkok', + 'Aden', + 'Khartoum' + ]; // 21 - 30 + if (temperature > 30) return 'Mecca'; + return cities[temperature + 5] || null; } function dragBurgLabel() { - const tr = parseTransform(this.getAttribute("transform")); - const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y; + const tr = parseTransform(this.getAttribute('transform')); + const dx = +tr[0] - d3.event.x, + dy = +tr[1] - d3.event.y; - d3.event.on("drag", function() { - const x = d3.event.x, y = d3.event.y; - this.setAttribute("transform", `translate(${(dx+x)},${(dy+y)})`); - tip('Use dragging for fine-tuning only, to actually move burg use "Relocate" button', false, "warning"); + d3.event.on('drag', function () { + const x = d3.event.x, + y = d3.event.y; + this.setAttribute('transform', `translate(${dx + x},${dy + y})`); + tip('Use dragging for fine-tuning only, to actually move burg use "Relocate" button', false, 'warning'); }); } function showGroupSection() { - document.querySelectorAll("#burgBottom > button").forEach(el => el.style.display = "none"); - document.getElementById("burgGroupSection").style.display = "inline-block"; + document.querySelectorAll('#burgBottom > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('burgGroupSection').style.display = 'inline-block'; } function hideGroupSection() { - document.querySelectorAll("#burgBottom > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("burgGroupSection").style.display = "none"; - document.getElementById("burgInputGroup").style.display = "none"; - document.getElementById("burgInputGroup").value = ""; - document.getElementById("burgSelectGroup").style.display = "inline-block"; + document.querySelectorAll('#burgBottom > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('burgGroupSection').style.display = 'none'; + document.getElementById('burgInputGroup').style.display = 'none'; + document.getElementById('burgInputGroup').value = ''; + document.getElementById('burgSelectGroup').style.display = 'inline-block'; } function changeGroup() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); moveBurgToGroup(id, this.value); } function toggleNewGroupInput() { - if (burgInputGroup.style.display === "none") { - burgInputGroup.style.display = "inline-block"; + if (burgInputGroup.style.display === 'none') { + burgInputGroup.style.display = 'inline-block'; burgInputGroup.focus(); - burgSelectGroup.style.display = "none"; + burgSelectGroup.style.display = 'none'; } else { - burgInputGroup.style.display = "none"; - burgSelectGroup.style.display = "inline-block"; + burgInputGroup.style.display = 'none'; + burgSelectGroup.style.display = 'inline-block'; } } function createNewGroup() { - if (!this.value) {tip("Please provide a valid group name", false, "error"); return;} - const group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (!this.value) { + tip('Please provide a valid group name', false, 'error'); + return; + } + const group = this.value + .toLowerCase() + .replace(/ /g, '_') + .replace(/[^\w\s]/gi, ''); if (document.getElementById(group)) { - tip("Element with this id already exists. Please provide a unique name", false, "error"); + tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } if (Number.isFinite(+group.charAt(0))) { - tip("Group name should start with a letter", false, "error"); + tip('Group name should start with a letter', false, 'error'); return; } - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); const oldGroup = elSelected.node().parentNode.id; const label = document.querySelector("#burgLabels [data-id='" + id + "']"); const icon = document.querySelector("#burgIcons [data-id='" + id + "']"); const anchor = document.querySelector("#anchors [data-id='" + id + "']"); - if (!label || !icon) {ERROR && console.error("Cannot find label or icon elements"); return;} + if (!label || !icon) { + ERROR && console.error('Cannot find label or icon elements'); + return; + } - const labelG = document.querySelector("#burgLabels > #"+oldGroup); - const iconG = document.querySelector("#burgIcons > #"+oldGroup); - const anchorG = document.querySelector("#anchors > #"+oldGroup); + const labelG = document.querySelector('#burgLabels > #' + oldGroup); + const iconG = document.querySelector('#burgIcons > #' + oldGroup); + const anchorG = document.querySelector('#anchors > #' + oldGroup); // just rename if only 1 element left const count = elSelected.node().parentNode.childElementCount; - if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) { - document.getElementById("burgSelectGroup").selectedOptions[0].remove(); - document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true)); + if (oldGroup !== 'cities' && oldGroup !== 'towns' && count === 1) { + document.getElementById('burgSelectGroup').selectedOptions[0].remove(); + document.getElementById('burgSelectGroup').options.add(new Option(group, group, false, true)); toggleNewGroupInput(); - document.getElementById("burgInputGroup").value = ""; + document.getElementById('burgInputGroup').value = ''; labelG.id = group; iconG.id = group; if (anchor) anchorG.id = group; @@ -202,16 +248,16 @@ function editBurg(id) { } // create new groups - document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true)); + document.getElementById('burgSelectGroup').options.add(new Option(group, group, false, true)); toggleNewGroupInput(); - document.getElementById("burgInputGroup").value = ""; + document.getElementById('burgInputGroup').value = ''; - const newLabelG = document.querySelector("#burgLabels").appendChild(labelG.cloneNode(false)); + const newLabelG = document.querySelector('#burgLabels').appendChild(labelG.cloneNode(false)); newLabelG.id = group; - const newIconG = document.querySelector("#burgIcons").appendChild(iconG.cloneNode(false)); + const newIconG = document.querySelector('#burgIcons').appendChild(iconG.cloneNode(false)); newIconG.id = group; if (anchor) { - const newAnchorG = document.querySelector("#anchors").appendChild(anchorG.cloneNode(false)); + const newAnchorG = document.querySelector('#anchors').appendChild(anchorG.cloneNode(false)); newAnchorG.id = group; } moveBurgToGroup(id, group); @@ -219,161 +265,168 @@ function editBurg(id) { function removeBurgsGroup() { const group = elSelected.node().parentNode; - const basic = group.id === "cities" || group.id === "towns"; + const basic = group.id === 'cities' || group.id === 'towns'; const burgsInGroup = []; - for (let i=0; i < group.children.length; i++) { + for (let i = 0; i < group.children.length; i++) { burgsInGroup.push(+group.children[i].dataset.id); } - const burgsToRemove = burgsInGroup.filter(b => !(pack.burgs[b].capital || pack.burgs[b].lock)); + const burgsToRemove = burgsInGroup.filter((b) => !(pack.burgs[b].capital || pack.burgs[b].lock)); const capital = burgsToRemove.length < burgsInGroup.length; - alertMessage.innerHTML = `Are you sure you want to remove - ${basic || capital ? "all unlocked elements in the group" : "the entire burg group"}? -
Please note that capital or locked burgs will not be deleted. -

Burgs to be removed: ${burgsToRemove.length}`; - $("#alert").dialog({resizable: false, title: "Remove route group", - buttons: { - Remove: function() { - $(this).dialog("close"); - $("#burgEditor").dialog("close"); - hideGroupSection(); - burgsToRemove.forEach(b => removeBurg(b)); + const message = `Are you sure you want to remove + ${basic || capital ? 'all unlocked elements in the group' : 'the entire burg group'}? +
Please note that capital or locked burgs will not be deleted. +

Burgs to be removed: ${burgsToRemove.length}`; + confirmationDialog({title: 'Remove burg group', message, confirm: 'Remove', onConfirm: removeGroup}); - if (!basic && !capital) { - // entirely remove group - const labelG = document.querySelector("#burgLabels > #"+group.id); - const iconG = document.querySelector("#burgIcons > #"+group.id); - const anchorG = document.querySelector("#anchors > #"+group.id); - if (labelG) labelG.remove(); - if (iconG) iconG.remove(); - if (anchorG) anchorG.remove(); - } - }, - Cancel: function() {$(this).dialog("close");} + function removeGroup() { + $(this).dialog('close'); + $('#burgEditor').dialog('close'); + hideGroupSection(); + burgsToRemove.forEach((b) => removeBurg(b)); + + if (!basic && !capital) { + // entirely remove group + const labelG = document.querySelector('#burgLabels > #' + group.id); + const iconG = document.querySelector('#burgIcons > #' + group.id); + const anchorG = document.querySelector('#anchors > #' + group.id); + if (labelG) labelG.remove(); + if (iconG) iconG.remove(); + if (anchorG) anchorG.remove(); } - }); + } } function changeName() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); pack.burgs[id].name = burgName.value; elSelected.text(burgName.value); } function generateNameRandom() { - const base = rand(nameBases.length-1); + const base = rand(nameBases.length - 1); burgName.value = Names.getBase(base); changeName(); } function changeType() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); pack.burgs[id].type = this.value; } function changeCulture() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); pack.burgs[id].culture = +this.value; } function generateNameCulture() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); const culture = pack.burgs[id].culture; burgName.value = Names.getCulture(culture); changeName(); } function changePopulation() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); pack.burgs[id].population = rn(burgPopulation.value / populationRate.value / urbanization.value, 4); } function toggleFeature() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); const b = pack.burgs[id]; const feature = this.dataset.feature; - const turnOn = this.classList.contains("inactive"); - if (feature === "port") togglePort(id); - else if(feature === "capital") toggleCapital(id); + const turnOn = this.classList.contains('inactive'); + if (feature === 'port') togglePort(id); + else if (feature === 'capital') toggleCapital(id); else b[feature] = +turnOn; - if (b[feature]) this.classList.remove("inactive"); - else if (!b[feature]) this.classList.add("inactive"); + if (b[feature]) this.classList.remove('inactive'); + else if (!b[feature]) this.classList.add('inactive'); - if (b.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block"; - else document.getElementById("burgEditAnchorStyle").style.display = "none"; + if (b.port) document.getElementById('burgEditAnchorStyle').style.display = 'inline-block'; + else document.getElementById('burgEditAnchorStyle').style.display = 'none'; } function toggleBurgLockButton() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); toggleBurgLock(id); updateBurgLockIcon(); } function updateBurgLockIcon() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); const b = pack.burgs[id]; - if (b.lock) {document.getElementById("burgLock").classList.remove("icon-lock-open"); document.getElementById("burgLock").classList.add("icon-lock");} - else {document.getElementById("burgLock").classList.remove("icon-lock"); document.getElementById("burgLock").classList.add("icon-lock-open");} - + if (b.lock) { + document.getElementById('burgLock').classList.remove('icon-lock-open'); + document.getElementById('burgLock').classList.add('icon-lock'); + } else { + document.getElementById('burgLock').classList.remove('icon-lock'); + document.getElementById('burgLock').classList.add('icon-lock-open'); + } } function showBurgELockTip() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); showBurgLockTip(id); } function showStyleSection() { - document.querySelectorAll("#burgBottom > button").forEach(el => el.style.display = "none"); - document.getElementById("burgStyleSection").style.display = "inline-block"; + document.querySelectorAll('#burgBottom > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('burgStyleSection').style.display = 'inline-block'; } function hideStyleSection() { - document.querySelectorAll("#burgBottom > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("burgStyleSection").style.display = "none"; + document.querySelectorAll('#burgBottom > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('burgStyleSection').style.display = 'none'; } function editGroupLabelStyle() { const g = elSelected.node().parentNode.id; - editStyle("labels", g); + editStyle('labels', g); } function editGroupIconStyle() { const g = elSelected.node().parentNode.id; - editStyle("burgIcons", g); + editStyle('burgIcons', g); } function editGroupAnchorStyle() { const g = elSelected.node().parentNode.id; - editStyle("anchors", g); + editStyle('anchors', g); } function openInMFCG(event) { - const id = elSelected.attr("data-id"); + const id = elSelected.attr('data-id'); const burg = pack.burgs[id]; const defSeed = +(seed + id.padStart(4, 0)); if (isCtrlClick(event)) { - prompt(`Please provide a Medieval Fantasy City Generator seed. + prompt( + `Please provide a Medieval Fantasy City Generator seed. Seed should be a number. Default seed is FMG map seed + burg id padded to 4 chars with zeros (${defSeed}). Please note that if seed is custom, "Overworld" button from MFCG will open a different map`, - {default:burg.MFCG||defSeed, step:1, min:1, max:1e13-1}, v => { - burg.MFCG = v; - openMFCG(v); - }); + {default: burg.MFCG || defSeed, step: 1, min: 1, max: 1e13 - 1}, + (v) => { + burg.MFCG = v; + openMFCG(v); + } + ); } else openMFCG(); function openMFCG(seed) { - if (!seed && burg.MFCGlink) {openURL(burg.MFCGlink); return;} + if (!seed && burg.MFCGlink) { + openURL(burg.MFCGlink); + return; + } const cells = pack.cells; const name = elSelected.text(); const size = Math.max(Math.min(rn(burg.population), 100), 6); // to be removed once change on MFDC is done const population = rn(burg.population * populationRate.value * urbanization.value); - + const s = burg.MFCG || defSeed; const cell = burg.cell; const hub = +cells.road[cell] > 50; const river = cells.r[cell] ? 1 : 0; - + const coast = +burg.port; const citadel = +burg.citadel; const walls = +burg.walls; @@ -381,38 +434,45 @@ function editBurg(id) { const temple = +burg.temple; const shanty = +burg.shanty; - const sea = coast && cells.haven[burg.cell] ? getSeaDirections(burg.cell) : ""; + const sea = coast && cells.haven[burg.cell] ? getSeaDirections(burg.cell) : ''; function getSeaDirections(i) { const p1 = cells.p[i]; const p2 = cells.p[cells.haven[i]]; - let deg = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180 / Math.PI - 90; + let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90; if (deg < 0) deg += 360; const norm = rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east - return "&sea="+norm; + return '&sea=' + norm; } - const site = "http://fantasycities.watabou.ru/?random=0&continuous=0"; + const site = 'http://fantasycities.watabou.ru/?random=0&continuous=0'; const url = `${site}&name=${name}&population=${population}&size=${size}&seed=${s}&hub=${hub}&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`; openURL(url); } } function openEmblemEdit() { - const id = +elSelected.attr("data-id"), burg = pack.burgs[id]; - editEmblem("burg", "burgCOA"+id, burg); + const id = +elSelected.attr('data-id'), + burg = pack.burgs[id]; + editEmblem('burg', 'burgCOA' + id, burg); } function toggleRelocateBurg() { - const toggler = document.getElementById("toggleCells"); - document.getElementById("burgRelocate").classList.toggle("pressed"); - if (document.getElementById("burgRelocate").classList.contains("pressed")) { - viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick); - tip("Click on map to relocate burg. Hold Shift for continuous move", true); - if (!layerIsOn("toggleCells")) {toggleCells(); toggler.dataset.forced = true;} + const toggler = document.getElementById('toggleCells'); + document.getElementById('burgRelocate').classList.toggle('pressed'); + if (document.getElementById('burgRelocate').classList.contains('pressed')) { + viewbox.style('cursor', 'crosshair').on('click', relocateBurgOnClick); + tip('Click on map to relocate burg. Hold Shift for continuous move', true); + if (!layerIsOn('toggleCells')) { + toggleCells(); + toggler.dataset.forced = true; + } } else { clearMainTip(); - viewbox.on("click", clicked).style("cursor", "default"); - if (layerIsOn("toggleCells") && toggler.dataset.forced) {toggleCells(); toggler.dataset.forced = false;} + viewbox.on('click', clicked).style('cursor', 'default'); + if (layerIsOn('toggleCells') && toggler.dataset.forced) { + toggleCells(); + toggler.dataset.forced = false; + } } } @@ -420,16 +480,16 @@ function editBurg(id) { const cells = pack.cells; const point = d3.mouse(this); const cell = findCell(point[0], point[1]); - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); const burg = pack.burgs[id]; if (cells.h[cell] < 20) { - tip("Cannot place burg into the water! Select a land cell", false, "error"); + tip('Cannot place burg into the water! Select a land cell', false, 'error'); return; } if (cells.burg[cell] && cells.burg[cell] !== id) { - tip("There is already a burg in this cell. Please select a free cell", false, "error"); + tip('There is already a burg in this cell. Please select a free cell', false, 'error'); return; } @@ -437,20 +497,29 @@ function editBurg(id) { const oldState = burg.state; if (newState !== oldState && burg.capital) { - tip("Capital cannot be relocated into another state!", false, "error"); + tip('Capital cannot be relocated into another state!', false, 'error'); return; } // change UI - const x = rn(point[0], 2), y = rn(point[1], 2); - burgIcons.select("[data-id='" + id + "']").attr("transform", null).attr("cx", x).attr("cy", y); - burgLabels.select("text[data-id='" + id + "']").attr("transform", null).attr("x", x).attr("y", y); - const anchor = anchors.select("use[data-id='" + id+ "']"); + const x = rn(point[0], 2), + y = rn(point[1], 2); + burgIcons + .select("[data-id='" + id + "']") + .attr('transform', null) + .attr('cx', x) + .attr('cy', y); + burgLabels + .select("text[data-id='" + id + "']") + .attr('transform', null) + .attr('x', x) + .attr('y', y); + const anchor = anchors.select("use[data-id='" + id + "']"); if (anchor.size()) { - const size = anchor.attr("width"); + const size = anchor.attr('width'); const xa = rn(x - size * 0.47, 2); const ya = rn(y - size * 0.47, 2); - anchor.attr("transform", null).attr("x", xa).attr("y", ya); + anchor.attr('transform', null).attr('x', xa).attr('y', ya); } // change data @@ -466,38 +535,38 @@ function editBurg(id) { } function editBurgLegend() { - const id = elSelected.attr("data-id"); + const id = elSelected.attr('data-id'); const name = elSelected.text(); - editNotes("burg"+id, name); + editNotes('burg' + id, name); } function removeSelectedBurg() { - const id = +elSelected.attr("data-id"); + const id = +elSelected.attr('data-id'); if (pack.burgs[id].capital) { alertMessage.innerHTML = `You cannot remove the burg as it is a state capital.

- You can change the capital using Burgs Editor (shift + T)`; - $("#alert").dialog({resizable: false, title: "Remove burg", - buttons: {Ok: function() {$(this).dialog("close");}} - }); - } else { - alertMessage.innerHTML = "Are you sure you want to remove the burg?"; - $("#alert").dialog({resizable: false, title: "Remove burg", + Please change state capital first. You can do it using Burgs Editor (shift + T)`; + $('#alert').dialog({ + resizable: false, + title: 'Remove burg', buttons: { - Remove: function() { - $(this).dialog("close"); - removeBurg(id); // see Editors module - $("#burgEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} + Ok: function () { + $(this).dialog('close'); + } } }); + } else { + const message = 'Are you sure you want to remove the burg?
This action cannot be reverted'; + const onConfirm = () => { + removeBurg(id); + $('#burgEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove burg', message, confirm: 'Remove', onConfirm}); } } function closeBurgEditor() { - document.getElementById("burgRelocate").classList.remove("pressed"); - burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false); + document.getElementById('burgRelocate').classList.remove('pressed'); + burgLabels.selectAll('text').call(d3.drag().on('drag', null)).classed('draggable', false); unselect(); } - } diff --git a/modules/ui/burgs-overview.js b/modules/ui/burgs-overview.js index 18bc79ce..dc74873a 100644 --- a/modules/ui/burgs-overview.js +++ b/modules/ui/burgs-overview.js @@ -1,34 +1,39 @@ -"use strict"; +'use strict'; function overviewBurgs() { if (customization) return; - closeDialogs("#burgsOverview, .stable"); - if (!layerIsOn("toggleIcons")) toggleIcons(); - if (!layerIsOn("toggleLabels")) toggleLabels(); + closeDialogs('#burgsOverview, .stable'); + if (!layerIsOn('toggleIcons')) toggleIcons(); + if (!layerIsOn('toggleLabels')) toggleLabels(); - const body = document.getElementById("burgsBody"); + const body = document.getElementById('burgsBody'); updateFilter(); burgsOverviewAddLines(); - $("#burgsOverview").dialog(); + $('#burgsOverview').dialog(); if (modules.overviewBurgs) return; modules.overviewBurgs = true; - $("#burgsOverview").dialog({ - title: "Burgs Overview", resizable: false, width: fitContent(), close: exitAddBurgMode, - position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} + $('#burgsOverview').dialog({ + title: 'Burgs Overview', + resizable: false, + width: fitContent(), + close: exitAddBurgMode, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'} }); // add listeners - document.getElementById("burgsOverviewRefresh").addEventListener("click", refreshBurgsEditor); - document.getElementById("burgsChart").addEventListener("click", showBurgsChart); - document.getElementById("burgsFilterState").addEventListener("change", burgsOverviewAddLines); - document.getElementById("burgsFilterCulture").addEventListener("change", burgsOverviewAddLines); - document.getElementById("regenerateBurgNames").addEventListener("click", regenerateNames); - document.getElementById("addNewBurg").addEventListener("click", enterAddBurgMode); - document.getElementById("burgsExport").addEventListener("click", downloadBurgsData); - document.getElementById("burgNamesImport").addEventListener("click", renameBurgsInBulk); - document.getElementById("burgsListToLoad").addEventListener("change", function() {uploadFile(this, importBurgNames)}); - document.getElementById("burgsRemoveAll").addEventListener("click", triggerAllBurgsRemove); + document.getElementById('burgsOverviewRefresh').addEventListener('click', refreshBurgsEditor); + document.getElementById('burgsChart').addEventListener('click', showBurgsChart); + document.getElementById('burgsFilterState').addEventListener('change', burgsOverviewAddLines); + document.getElementById('burgsFilterCulture').addEventListener('change', burgsOverviewAddLines); + document.getElementById('regenerateBurgNames').addEventListener('click', regenerateNames); + document.getElementById('addNewBurg').addEventListener('click', enterAddBurgMode); + document.getElementById('burgsExport').addEventListener('click', downloadBurgsData); + document.getElementById('burgNamesImport').addEventListener('click', renameBurgsInBulk); + document.getElementById('burgsListToLoad').addEventListener('change', function () { + uploadFile(this, importBurgNames); + }); + document.getElementById('burgsRemoveAll').addEventListener('click', triggerAllBurgsRemove); function refreshBurgsEditor() { updateFilter(); @@ -36,41 +41,42 @@ function overviewBurgs() { } function updateFilter() { - const stateFilter = document.getElementById("burgsFilterState"); + const stateFilter = document.getElementById('burgsFilterState'); const selectedState = stateFilter.value || 1; stateFilter.options.length = 0; // remove all options stateFilter.options.add(new Option(`all`, -1, false, selectedState == -1)); stateFilter.options.add(new Option(pack.states[0].name, 0, false, !selectedState)); - const statesSorted = pack.states.filter(s => s.i && !s.removed).sort((a, b) => (a.name > b.name) ? 1 : -1); - statesSorted.forEach(s => stateFilter.options.add(new Option(s.name, s.i, false, s.i == selectedState))); + const statesSorted = pack.states.filter((s) => s.i && !s.removed).sort((a, b) => (a.name > b.name ? 1 : -1)); + statesSorted.forEach((s) => stateFilter.options.add(new Option(s.name, s.i, false, s.i == selectedState))); - const cultureFilter = document.getElementById("burgsFilterCulture"); + const cultureFilter = document.getElementById('burgsFilterCulture'); const selectedCulture = cultureFilter.value || -1; cultureFilter.options.length = 0; // remove all options cultureFilter.options.add(new Option(`all`, -1, false, selectedCulture == -1)); cultureFilter.options.add(new Option(pack.cultures[0].name, 0, false, !selectedCulture)); - const culturesSorted = pack.cultures.filter(c => c.i && !c.removed).sort((a, b) => (a.name > b.name) ? 1 : -1); - culturesSorted.forEach(c => cultureFilter.options.add(new Option(c.name, c.i, false, c.i == selectedCulture))); + const culturesSorted = pack.cultures.filter((c) => c.i && !c.removed).sort((a, b) => (a.name > b.name ? 1 : -1)); + culturesSorted.forEach((c) => cultureFilter.options.add(new Option(c.name, c.i, false, c.i == selectedCulture))); } // add line for each burg function burgsOverviewAddLines() { - const selectedState = +document.getElementById("burgsFilterState").value; - const selectedCulture = +document.getElementById("burgsFilterCulture").value; - let filtered = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs - if (selectedState != -1) filtered = filtered.filter(b => b.state === selectedState); // filtered by state - if (selectedCulture != -1) filtered = filtered.filter(b => b.culture === selectedCulture); // filtered by culture + const selectedState = +document.getElementById('burgsFilterState').value; + const selectedCulture = +document.getElementById('burgsFilterCulture').value; + let filtered = pack.burgs.filter((b) => b.i && !b.removed); // all valid burgs + if (selectedState != -1) filtered = filtered.filter((b) => b.state === selectedState); // filtered by state + if (selectedCulture != -1) filtered = filtered.filter((b) => b.culture === selectedCulture); // filtered by culture - body.innerHTML = ""; - let lines = "", totalPopulation = 0; + body.innerHTML = ''; + let lines = '', + totalPopulation = 0; for (const b of filtered) { const population = b.population * populationRate.value * urbanization.value; totalPopulation += population; - const type = b.capital && b.port ? "a-capital-port" : b.capital ? "c-capital" : b.port ? "p-port" : "z-burg"; + const type = b.capital && b.port ? 'a-capital-port' : b.capital ? 'c-capital' : b.port ? 'p-port' : 'z-burg'; const state = pack.states[b.state].name; const prov = pack.cells.province[b.cell]; - const province = prov ? pack.provinces[prov].name : ""; + const province = prov ? pack.provinces[prov].name : ''; const culture = pack.cultures[b.culture].name; lines += `
@@ -90,47 +96,47 @@ function overviewBurgs() {
`; } - body.insertAdjacentHTML("beforeend", lines); + body.insertAdjacentHTML('beforeend', lines); // update footer burgsFooterBurgs.innerHTML = filtered.length; burgsFooterPopulation.innerHTML = filtered.length ? si(totalPopulation / filtered.length) : 0; // add listeners - body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => burgHighlightOn(ev))); - body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => burgHighlightOff(ev))); - body.querySelectorAll("div > input.burgName").forEach(el => el.addEventListener("input", changeBurgName)); - body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomIntoBurg)); - body.querySelectorAll("div > select.stateCulture").forEach(el => el.addEventListener("change", changeBurgCulture)); - body.querySelectorAll("div > input.burgPopulation").forEach(el => el.addEventListener("change", changeBurgPopulation)); - body.querySelectorAll("div > span.icon-star-empty").forEach(el => el.addEventListener("click", toggleCapitalStatus)); - body.querySelectorAll("div > span.icon-anchor").forEach(el => el.addEventListener("click", togglePortStatus)); - body.querySelectorAll("div > span.locks").forEach(el => el.addEventListener("click", toggleBurgLockStatus)); - body.querySelectorAll("div > span.locks").forEach(el => el.addEventListener("mouseover", showBurgOLockTip)); - body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openBurgEditor)); - body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", triggerBurgRemove)); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('mouseenter', (ev) => burgHighlightOn(ev))); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('mouseleave', (ev) => burgHighlightOff(ev))); + body.querySelectorAll('div > input.burgName').forEach((el) => el.addEventListener('input', changeBurgName)); + body.querySelectorAll('div > span.icon-dot-circled').forEach((el) => el.addEventListener('click', zoomIntoBurg)); + body.querySelectorAll('div > select.stateCulture').forEach((el) => el.addEventListener('change', changeBurgCulture)); + body.querySelectorAll('div > input.burgPopulation').forEach((el) => el.addEventListener('change', changeBurgPopulation)); + body.querySelectorAll('div > span.icon-star-empty').forEach((el) => el.addEventListener('click', toggleCapitalStatus)); + body.querySelectorAll('div > span.icon-anchor').forEach((el) => el.addEventListener('click', togglePortStatus)); + body.querySelectorAll('div > span.locks').forEach((el) => el.addEventListener('click', toggleBurgLockStatus)); + body.querySelectorAll('div > span.locks').forEach((el) => el.addEventListener('mouseover', showBurgOLockTip)); + body.querySelectorAll('div > span.icon-pencil').forEach((el) => el.addEventListener('click', openBurgEditor)); + body.querySelectorAll('div > span.icon-trash-empty').forEach((el) => el.addEventListener('click', triggerBurgRemove)); applySorting(burgsHeader); } function getCultureOptions(culture) { - let options = ""; - pack.cultures.filter(c => !c.removed).forEach(c => options += ``); + let options = ''; + pack.cultures.filter((c) => !c.removed).forEach((c) => (options += ``)); return options; } function burgHighlightOn(event) { - if (!layerIsOn("toggleLabels")) toggleLabels(); + if (!layerIsOn('toggleLabels')) toggleLabels(); const burg = +event.target.dataset.id; - burgLabels.select("[data-id='" + burg + "']").classed("drag", true); + burgLabels.select("[data-id='" + burg + "']").classed('drag', true); } function burgHighlightOff() { - burgLabels.selectAll("text.drag").classed("drag", false); + burgLabels.selectAll('text.drag').classed('drag', false); } function changeBurgName() { - if (this.value == "")tip("Please provide a name", false, "error"); + if (this.value == '') tip('Please provide a name', false, 'error'); const burg = +this.parentNode.dataset.id; pack.burgs[burg].name = this.value; this.parentNode.dataset.name = this.value; @@ -141,7 +147,8 @@ function overviewBurgs() { function zoomIntoBurg() { const burg = +this.parentNode.dataset.id; const label = document.querySelector("#burgLabels [data-id='" + burg + "']"); - const x = +label.getAttribute("x"), y = +label.getAttribute("y"); + const x = +label.getAttribute('x'), + y = +label.getAttribute('y'); zoomTo(x, y, 8, 2000); } @@ -154,8 +161,8 @@ function overviewBurgs() { function changeBurgPopulation() { const burg = +this.parentNode.dataset.id; - if (this.value == "" || isNaN(+this.value)) { - tip("Please provide an integer number (like 10000, not 10K)", false, "error"); + if (this.value == '' || isNaN(+this.value)) { + tip('Please provide an integer number (like 10000, not 10K)', false, 'error'); this.value = si(pack.burgs[burg].population * populationRate.value * urbanization.value); return; } @@ -164,7 +171,7 @@ function overviewBurgs() { this.value = si(this.value); const population = []; - body.querySelectorAll(":scope > div").forEach(el => population.push(+getInteger(el.dataset.population))); + body.querySelectorAll(':scope > div').forEach((el) => population.push(+getInteger(el.dataset.population))); burgsFooterPopulation.innerHTML = si(d3.mean(population)); } @@ -177,15 +184,22 @@ function overviewBurgs() { function togglePortStatus() { const burg = +this.parentNode.parentNode.dataset.id; togglePort(burg); - if (this.classList.contains("inactive")) this.classList.remove("inactive"); - else this.classList.add("inactive"); + if (this.classList.contains('inactive')) this.classList.remove('inactive'); + else this.classList.add('inactive'); } function toggleBurgLockStatus() { const burg = +this.parentNode.dataset.id; toggleBurgLock(burg); - if (this.classList.contains("icon-lock")) {this.classList.remove("icon-lock"); this.classList.add("icon-lock-open"); this.classList.add("inactive");} - else {this.classList.remove("icon-lock-open"); this.classList.add("icon-lock"); this.classList.remove("inactive");} + if (this.classList.contains('icon-lock')) { + this.classList.remove('icon-lock'); + this.classList.add('icon-lock-open'); + this.classList.add('inactive'); + } else { + this.classList.remove('icon-lock-open'); + this.classList.add('icon-lock'); + this.classList.remove('inactive'); + } } function showBurgOLockTip() { @@ -200,29 +214,27 @@ function overviewBurgs() { function triggerBurgRemove() { const burg = +this.parentNode.dataset.id; - if (pack.burgs[burg].capital) {tip("You cannot remove the capital. Please change the capital first", false, "error"); return;} + if (pack.burgs[burg].capital) { + tip('You cannot remove the capital. Please change the capital first', false, 'error'); + return; + } - alertMessage.innerHTML = "Are you sure you want to remove the burg?"; - $("#alert").dialog({resizable: false, title: "Remove burg", - buttons: { - Remove: function() { - $(this).dialog("close"); - removeBurg(burg); - burgsOverviewAddLines(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the burg?
This action cannot be reverted'; + const onConfirm = () => { + removeBurg(burg); + burgsOverviewAddLines(); + }; + confirmationDialog({title: 'Remove burg', message, confirm: 'Remove', onConfirm}); } function regenerateNames() { - body.querySelectorAll(":scope > div").forEach(function(el) { + body.querySelectorAll(':scope > div').forEach(function (el) { const burg = +el.dataset.id; //if (pack.burgs[burg].lock) return; const culture = pack.burgs[burg].culture; const name = Names.getCulture(culture); if (!pack.burgs[burg].lock) { - el.querySelector(".burgName").value = name; + el.querySelector('.burgName').value = name; pack.burgs[burg].name = el.dataset.name = name; burgLabels.select("[data-id='" + burg + "']").text(name); } @@ -230,18 +242,27 @@ function overviewBurgs() { } function enterAddBurgMode() { - if (this.classList.contains("pressed")) {exitAddBurgMode(); return;}; + if (this.classList.contains('pressed')) { + exitAddBurgMode(); + return; + } customization = 3; - this.classList.add("pressed"); - tip("Click on the map to create a new burg. Hold Shift to add multiple", true, "warn"); - viewbox.style("cursor", "crosshair").on("click", addBurgOnClick); + this.classList.add('pressed'); + tip('Click on the map to create a new burg. Hold Shift to add multiple', true, 'warn'); + viewbox.style('cursor', 'crosshair').on('click', addBurgOnClick); } function addBurgOnClick() { const point = d3.mouse(this); const cell = findCell(point[0], point[1]); - if (pack.cells.h[cell] < 20) {tip("You cannot place state into the water. Please click on a land cell", false, "error"); return;} - if (pack.cells.burg[cell]) {tip("There is already a burg in this cell. Please select a free cell", false, "error"); return;} + if (pack.cells.h[cell] < 20) { + tip('You cannot place state into the water. Please click on a land cell', false, 'error'); + return; + } + if (pack.cells.burg[cell]) { + tip('There is already a burg in this cell. Please select a free cell', false, 'error'); + return; + } addBurg(point); // add new burg if (d3.event.shiftKey === false) { @@ -254,31 +275,37 @@ function overviewBurgs() { customization = 0; restoreDefaultEvents(); clearMainTip(); - if (addBurgTool.classList.contains("pressed")) addBurgTool.classList.remove("pressed"); - if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed"); + if (addBurgTool.classList.contains('pressed')) addBurgTool.classList.remove('pressed'); + if (addNewBurg.classList.contains('pressed')) addNewBurg.classList.remove('pressed'); } function showBurgsChart() { // build hierarchy tree - const states = pack.states.map(s => { - const color = s.color ? s.color : "#ccc"; + const states = pack.states.map((s) => { + const color = s.color ? s.color : '#ccc'; const name = s.fullName ? s.fullName : s.name; - return {id:s.i, state: s.i ? 0 : null, color, name} - }); - const burgs = pack.burgs.filter(b => b.i && !b.removed).map(b => { - const id = b.i+states.length-1; - const population = b.population; - const capital = b.capital; - const province = pack.cells.province[b.cell]; - const parent = province ? province + states.length-1 : b.state; - return {id, i:b.i, state:b.state, culture:b.culture, province, parent, name:b.name, population, capital, x:b.x, y:b.y} + return {id: s.i, state: s.i ? 0 : null, color, name}; }); + const burgs = pack.burgs + .filter((b) => b.i && !b.removed) + .map((b) => { + const id = b.i + states.length - 1; + const population = b.population; + const capital = b.capital; + const province = pack.cells.province[b.cell]; + const parent = province ? province + states.length - 1 : b.state; + return {id, i: b.i, state: b.state, culture: b.culture, province, parent, name: b.name, population, capital, x: b.x, y: b.y}; + }); const data = states.concat(burgs); - const root = d3.stratify().parentId(d => d.state)(data) - .sum(d => d.population).sort((a, b) => b.value - a.value); + const root = d3 + .stratify() + .parentId((d) => d.state)(data) + .sum((d) => d.population) + .sort((a, b) => b.value - a.value); - const width = 150 + 200 * uiSizeOutput.value, height = 150 + 200 * uiSizeOutput.value; + const width = 150 + 200 * uiSizeOutput.value, + height = 150 + 200 * uiSizeOutput.value; const margin = {top: 0, right: -50, bottom: -10, left: -50}; const w = width - margin.left - margin.right; const h = height - margin.top - margin.bottom; @@ -291,130 +318,155 @@ function overviewBurgs() { `; alertMessage.innerHTML += `
`; - const svg = d3.select("#alertMessage").insert("svg", "#burgsInfo").attr("id", "burgsTree") - .attr("width", width).attr("height", height-10).attr("stroke-width", 2); - const graph = svg.append("g").attr("transform", `translate(-50, -10)`); - document.getElementById("burgsTreeType").addEventListener("change", updateChart); + const svg = d3 + .select('#alertMessage') + .insert('svg', '#burgsInfo') + .attr('id', 'burgsTree') + .attr('width', width) + .attr('height', height - 10) + .attr('stroke-width', 2); + const graph = svg.append('g').attr('transform', `translate(-50, -10)`); + document.getElementById('burgsTreeType').addEventListener('change', updateChart); treeLayout(root); - const node = graph.selectAll("circle").data(root.leaves()) - .join("circle").attr("data-id", d => d.data.i) - .attr("r", d => d.r).attr("fill", d => d.parent.data.color) - .attr("cx", d => d.x).attr("cy", d => d.y) - .on("mouseenter", d => showInfo(event, d)) - .on("mouseleave", d => hideInfo(event, d)) - .on("click", d => zoomTo(d.data.x, d.data.y, 8, 2000)); + const node = graph + .selectAll('circle') + .data(root.leaves()) + .join('circle') + .attr('data-id', (d) => d.data.i) + .attr('r', (d) => d.r) + .attr('fill', (d) => d.parent.data.color) + .attr('cx', (d) => d.x) + .attr('cy', (d) => d.y) + .on('mouseenter', (d) => showInfo(event, d)) + .on('mouseleave', (d) => hideInfo(event, d)) + .on('click', (d) => zoomTo(d.data.x, d.data.y, 8, 2000)); function showInfo(ev, d) { - d3.select(ev.target).transition().duration(1500).attr("stroke", "#c13119"); + d3.select(ev.target).transition().duration(1500).attr('stroke', '#c13119'); const name = d.data.name; const parent = d.parent.data.name; const population = si(d.value * populationRate.value * urbanization.value); burgsInfo.innerHTML = `${name}. ${parent}. Population: ${population}`; burgHighlightOn(ev); - tip("Click to zoom into view"); + tip('Click to zoom into view'); } function hideInfo(ev) { burgHighlightOff(ev); - if (!document.getElementById("burgsInfo")) return; - burgsInfo.innerHTML = "‍"; - d3.select(ev.target).transition().attr("stroke", null); - tip(""); + if (!document.getElementById('burgsInfo')) return; + burgsInfo.innerHTML = '‍'; + d3.select(ev.target).transition().attr('stroke', null); + tip(''); } function updateChart() { - const getStatesData = () => pack.states.map(s => { - const color = s.color ? s.color : "#ccc"; - const name = s.fullName ? s.fullName : s.name; - return {id:s.i, state: s.i ? 0 : null, color, name} - }); + const getStatesData = () => + pack.states.map((s) => { + const color = s.color ? s.color : '#ccc'; + const name = s.fullName ? s.fullName : s.name; + return {id: s.i, state: s.i ? 0 : null, color, name}; + }); - const getCulturesData = () => pack.cultures.map(c => { - const color = c.color ? c.color : "#ccc"; - return {id:c.i, culture: c.i ? 0 : null, color, name:c.name} - }); + const getCulturesData = () => + pack.cultures.map((c) => { + const color = c.color ? c.color : '#ccc'; + return {id: c.i, culture: c.i ? 0 : null, color, name: c.name}; + }); const getParentData = () => { - const states = pack.states.map(s => { - const color = s.color ? s.color : "#ccc"; + const states = pack.states.map((s) => { + const color = s.color ? s.color : '#ccc'; const name = s.fullName ? s.fullName : s.name; - return {id:s.i, parent: s.i ? 0 : null, color, name} - }); - const provinces = pack.provinces.filter(p => p.i && !p.removed).map(p => { - return {id:p.i + states.length-1, parent: p.state, color:p.color, name:p.fullName} + return {id: s.i, parent: s.i ? 0 : null, color, name}; }); + const provinces = pack.provinces + .filter((p) => p.i && !p.removed) + .map((p) => { + return {id: p.i + states.length - 1, parent: p.state, color: p.color, name: p.fullName}; + }); return states.concat(provinces); - } + }; - const getProvincesData = () => pack.provinces.map(p => { - const color = p.color ? p.color : "#ccc"; - const name = p.fullName ? p.fullName : p.name; - return {id:p.i ? p.i : 0, province: p.i ? 0 : null, color, name} - }); + const getProvincesData = () => + pack.provinces.map((p) => { + const color = p.color ? p.color : '#ccc'; + const name = p.fullName ? p.fullName : p.name; + return {id: p.i ? p.i : 0, province: p.i ? 0 : null, color, name}; + }); - const value = d => { - if (this.value === "states") return d.state; - if (this.value === "cultures") return d.culture; - if (this.value === "parent") return d.parent; - if (this.value === "provinces") return d.province; - } + const value = (d) => { + if (this.value === 'states') return d.state; + if (this.value === 'cultures') return d.culture; + if (this.value === 'parent') return d.parent; + if (this.value === 'provinces') return d.province; + }; - const base = this.value === "states" ? getStatesData() - : this.value === "cultures" ? getCulturesData() - : this.value === "parent" ? getParentData() : getProvincesData(); - burgs.forEach(b => b.id = b.i+base.length-1); + const base = this.value === 'states' ? getStatesData() : this.value === 'cultures' ? getCulturesData() : this.value === 'parent' ? getParentData() : getProvincesData(); + burgs.forEach((b) => (b.id = b.i + base.length - 1)); const data = base.concat(burgs); - const root = d3.stratify().parentId(d => value(d))(data) - .sum(d => d.population).sort((a, b) => b.value - a.value); + const root = d3 + .stratify() + .parentId((d) => value(d))(data) + .sum((d) => d.population) + .sort((a, b) => b.value - a.value); - node.data(treeLayout(root).leaves()).transition().duration(2000) - .attr("data-id", d => d.data.i).attr("fill", d => d.parent.data.color) - .attr("cx", d => d.x).attr("cy", d => d.y).attr("r", d => d.r); + node + .data(treeLayout(root).leaves()) + .transition() + .duration(2000) + .attr('data-id', (d) => d.data.i) + .attr('fill', (d) => d.parent.data.color) + .attr('cx', (d) => d.x) + .attr('cy', (d) => d.y) + .attr('r', (d) => d.r); } - $("#alert").dialog({ - title: "Burgs bubble chart", width: fitContent(), - position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {}, - close: () => {alertMessage.innerHTML = "";} + $('#alert').dialog({ + title: 'Burgs bubble chart', + width: fitContent(), + position: {my: 'left bottom', at: 'left+10 bottom-10', of: 'svg'}, + buttons: {}, + close: () => { + alertMessage.innerHTML = ''; + } }); - } function downloadBurgsData() { - let data = "Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation ("+heightUnit.value+"),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town\n"; // headers - const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs + let data = 'Id,Burg,Province,State,Culture,Religion,Population,Longitude,Latitude,Elevation (' + heightUnit.value + '),Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town\n'; // headers + const valid = pack.burgs.filter((b) => b.i && !b.removed); // all valid burgs - valid.forEach(b => { - data += b.i + ","; - data += b.name + ","; + valid.forEach((b) => { + data += b.i + ','; + data += b.name + ','; const province = pack.cells.province[b.cell]; - data += province ? pack.provinces[province].fullName + "," : ","; - data += b.state ? pack.states[b.state].fullName +"," : pack.states[b.state].name + ","; - data += pack.cultures[b.culture].name + ","; - data += pack.religions[pack.cells.religion[b.cell]].name + ","; - data += rn(b.population * populationRate.value * urbanization.value) + ","; + data += province ? pack.provinces[province].fullName + ',' : ','; + data += b.state ? pack.states[b.state].fullName + ',' : pack.states[b.state].name + ','; + data += pack.cultures[b.culture].name + ','; + data += pack.religions[pack.cells.religion[b.cell]].name + ','; + data += rn(b.population * populationRate.value * urbanization.value) + ','; // add geography data - data += mapCoordinates.lonW + (b.x / graphWidth) * mapCoordinates.lonT + ","; - data += mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT + ","; // this is inverted in QGIS otherwise - data += parseInt(getHeight(pack.cells.h[b.cell])) + ","; + data += mapCoordinates.lonW + (b.x / graphWidth) * mapCoordinates.lonT + ','; + data += mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT + ','; // this is inverted in QGIS otherwise + data += parseInt(getHeight(pack.cells.h[b.cell])) + ','; // add status data - data += b.capital ? "capital," : ","; - data += b.port ? "port," : ","; - data += b.citadel ? "citadel," : ","; - data += b.walls ? "walls," : ","; - data += b.plaza ? "plaza," : ","; - data += b.temple ? "temple," : ","; - data += b.shanty ? "shanty town\n" : "\n"; + data += b.capital ? 'capital,' : ','; + data += b.port ? 'port,' : ','; + data += b.citadel ? 'citadel,' : ','; + data += b.walls ? 'walls,' : ','; + data += b.plaza ? 'plaza,' : ','; + data += b.temple ? 'temple,' : ','; + data += b.shanty ? 'shanty town\n' : '\n'; }); - const name = getFileName("Burgs") + ".csv"; + const name = getFileName('Burgs') + '.csv'; downloadFile(data, name); } @@ -423,49 +475,67 @@ function overviewBurgs() { If you do not want to change the name, just leave it as is`; alertMessage.innerHTML = message; - $("#alert").dialog({title: "Burgs bulk renaming", width:"22em", - position: {my: "center", at: "center", of: "svg"}, + $('#alert').dialog({ + title: 'Burgs bulk renaming', + width: '22em', + position: {my: 'center', at: 'center', of: 'svg'}, buttons: { - Download: function() { - const data = pack.burgs.filter(b => b.i && !b.removed).map(b => b.name).join("\r\n"); - const name = getFileName("Burg names") + ".txt"; + Download: function () { + const data = pack.burgs + .filter((b) => b.i && !b.removed) + .map((b) => b.name) + .join('\r\n'); + const name = getFileName('Burg names') + '.txt'; downloadFile(data, name); }, Upload: () => burgsListToLoad.click(), - Cancel: function() {$(this).dialog("close");} + Cancel: function () { + $(this).dialog('close'); + } } }); } function importBurgNames(dataLoaded) { - if (!dataLoaded) {tip("Cannot load the file, please check the format", false, "error"); return;} - const data = dataLoaded.split("\r\n"); - if (!data.length) {tip("Cannot parse the list, please check the file format", false, "error"); return;} + if (!dataLoaded) { + tip('Cannot load the file, please check the format', false, 'error'); + return; + } + const data = dataLoaded.split('\r\n'); + if (!data.length) { + tip('Cannot parse the list, please check the file format', false, 'error'); + return; + } - let change = [], message = `Burgs will be renamed as below. Please confirm`; + let change = [], + message = `Burgs will be renamed as below. Please confirm`; message += ``; - const burgs = pack.burgs.filter(b => b.i && !b.removed); - for (let i=0; i < data.length && i <= burgs.length; i++) { + const burgs = pack.burgs.filter((b) => b.i && !b.removed); + for (let i = 0; i < data.length && i <= burgs.length; i++) { const v = data[i]; if (!v || !burgs[i] || v == burgs[i].name) continue; - change.push({id:burgs[i].i, name: v}); + change.push({id: burgs[i].i, name: v}); message += ``; } message += `
IdCurrent nameNew Name
${burgs[i].i}${burgs[i].name}${v}
`; - if (!change.length) message = "No changes found in the file. Please change some names to get a result" + if (!change.length) message = 'No changes found in the file. Please change some names to get a result'; alertMessage.innerHTML = message; - $("#alert").dialog({title: "Burgs bulk renaming", width:"22em", - position: {my: "center", at: "center", of: "svg"}, + $('#alert').dialog({ + title: 'Burgs bulk renaming', + width: '22em', + position: {my: 'center', at: 'center', of: 'svg'}, buttons: { - Cancel: function() {$(this).dialog("close");}, - Confirm: function() { - for (let i=0; i < change.length; i++) { + Cancel: function () { + $(this).dialog('close'); + }, + Confirm: function () { + for (let i = 0; i < change.length; i++) { const id = change[i].id; pack.burgs[id].name = change[i].name; burgLabels.select("[data-id='" + id + "']").text(change[i].name); } - $(this).dialog("close"); + $(this).dialog('close'); burgsOverviewAddLines(); } } @@ -473,21 +543,12 @@ function overviewBurgs() { } function triggerAllBurgsRemove() { - alertMessage.innerHTML = `Are you sure you want to remove all unlocked burgs except for capitals? -
To remove a capital you have to remove a state first`; - $("#alert").dialog({resizable: false, title: "Remove all burgs", - buttons: { - Remove: function() { - $(this).dialog("close"); - removeAllBurgs(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove all unlocked burgs except for capitals?
To remove a capital you have to remove the state first'; + confirmationDialog({title: 'Remove all burgs', message, confirm: 'Remove', onConfirm: removeAllBurgs}); } function removeAllBurgs() { - pack.burgs.filter(b => b.i && !(b.capital || b.lock)).forEach(b => removeBurg(b.i)); + pack.burgs.filter((b) => b.i && !(b.capital || b.lock)).forEach((b) => removeBurg(b.i)); burgsOverviewAddLines(); } } diff --git a/modules/ui/coastline-editor.js b/modules/ui/coastline-editor.js index b1c357a9..18a22ba4 100644 --- a/modules/ui/coastline-editor.js +++ b/modules/ui/coastline-editor.js @@ -1,96 +1,118 @@ -"use strict"; +'use strict'; function editCoastline(node = d3.event.target) { if (customization) return; - closeDialogs(".stable"); - if (layerIsOn("toggleCells")) toggleCells(); + closeDialogs('.stable'); + if (layerIsOn('toggleCells')) toggleCells(); - $("#coastlineEditor").dialog({ - title: "Edit Coastline", resizable: false, - position: {my: "center top+20", at: "top", of: d3.event, collision: "fit"}, + $('#coastlineEditor').dialog({ + title: 'Edit Coastline', + resizable: false, + position: {my: 'center top+20', at: 'top', of: d3.event, collision: 'fit'}, close: closeCoastlineEditor }); - debug.append("g").attr("id", "vertices"); + debug.append('g').attr('id', 'vertices'); elSelected = d3.select(node); selectCoastlineGroup(node); drawCoastlineVertices(); - viewbox.on("touchmove mousemove", null); + viewbox.on('touchmove mousemove', null); if (modules.editCoastline) return; modules.editCoastline = true; // add listeners - document.getElementById("coastlineGroupsShow").addEventListener("click", showGroupSection); - document.getElementById("coastlineGroup").addEventListener("change", changeCoastlineGroup); - document.getElementById("coastlineGroupAdd").addEventListener("click", toggleNewGroupInput); - document.getElementById("coastlineGroupName").addEventListener("change", createNewGroup); - document.getElementById("coastlineGroupRemove").addEventListener("click", removeCoastlineGroup); - document.getElementById("coastlineGroupsHide").addEventListener("click", hideGroupSection); - document.getElementById("coastlineEditStyle").addEventListener("click", editGroupStyle); + document.getElementById('coastlineGroupsShow').addEventListener('click', showGroupSection); + document.getElementById('coastlineGroup').addEventListener('change', changeCoastlineGroup); + document.getElementById('coastlineGroupAdd').addEventListener('click', toggleNewGroupInput); + document.getElementById('coastlineGroupName').addEventListener('change', createNewGroup); + document.getElementById('coastlineGroupRemove').addEventListener('click', removeCoastlineGroup); + document.getElementById('coastlineGroupsHide').addEventListener('click', hideGroupSection); + document.getElementById('coastlineEditStyle').addEventListener('click', editGroupStyle); function drawCoastlineVertices() { - const f = +elSelected.attr("data-f"); // feature id + const f = +elSelected.attr('data-f'); // feature id const v = pack.features[f].vertices; // coastline outer vertices const l = pack.cells.i.length; - const c = [... new Set(v.map(v => pack.vertices.c[v]).flat())].filter(c => c < l); - debug.select("#vertices").selectAll("polygon").data(c).enter().append("polygon") - .attr("points", d => getPackPolygon(d)).attr("data-c", d => d); + const c = [...new Set(v.map((v) => pack.vertices.c[v]).flat())].filter((c) => c < l); + debug + .select('#vertices') + .selectAll('polygon') + .data(c) + .enter() + .append('polygon') + .attr('points', (d) => getPackPolygon(d)) + .attr('data-c', (d) => d); - debug.select("#vertices").selectAll("circle").data(v).enter().append("circle") - .attr("cx", d => pack.vertices.p[d][0]).attr("cy", d => pack.vertices.p[d][1]) - .attr("r", .4).attr("data-v", d => d).call(d3.drag().on("drag", dragVertex)) - .on("mousemove", () => tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights")); + debug + .select('#vertices') + .selectAll('circle') + .data(v) + .enter() + .append('circle') + .attr('cx', (d) => pack.vertices.p[d][0]) + .attr('cy', (d) => pack.vertices.p[d][1]) + .attr('r', 0.4) + .attr('data-v', (d) => d) + .call(d3.drag().on('drag', dragVertex)) + .on('mousemove', () => tip('Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights')); - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; const area = pack.features[f].area; coastlineArea.innerHTML = si(area * distanceScaleInput.value ** 2) + unit; } function dragVertex() { - const x = rn(d3.event.x, 2), y = rn(d3.event.y, 2); - this.setAttribute("cx", x); - this.setAttribute("cy", y); + const x = rn(d3.event.x, 2), + y = rn(d3.event.y, 2); + this.setAttribute('cx', x); + this.setAttribute('cy', y); const v = +this.dataset.v; pack.vertices.p[v] = [x, y]; - debug.select("#vertices").selectAll("polygon").attr("points", d => getPackPolygon(d)); + debug + .select('#vertices') + .selectAll('polygon') + .attr('points', (d) => getPackPolygon(d)); redrawCoastline(); } function redrawCoastline() { lineGen.curve(d3.curveBasisClosed); - const f = +elSelected.attr("data-f"); + const f = +elSelected.attr('data-f'); const vertices = pack.features[f].vertices; - const points = clipPoly(vertices.map(v => pack.vertices.p[v]), 1); + const points = clipPoly( + vertices.map((v) => pack.vertices.p[v]), + 1 + ); const d = round(lineGen(points)); - elSelected.attr("d", d); - defs.select("mask#land > path#land_"+f).attr("d", d); // update land mask - defs.select("mask#water > path#water_"+f).attr("d", d); // update water mask + elSelected.attr('d', d); + defs.select('mask#land > path#land_' + f).attr('d', d); // update land mask + defs.select('mask#water > path#water_' + f).attr('d', d); // update water mask - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; const area = Math.abs(d3.polygonArea(points)); coastlineArea.innerHTML = si(area * distanceScaleInput.value ** 2) + unit; } function showGroupSection() { - document.querySelectorAll("#coastlineEditor > button").forEach(el => el.style.display = "none"); - document.getElementById("coastlineGroupsSelection").style.display = "inline-block"; + document.querySelectorAll('#coastlineEditor > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('coastlineGroupsSelection').style.display = 'inline-block'; } function hideGroupSection() { - document.querySelectorAll("#coastlineEditor > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("coastlineGroupsSelection").style.display = "none"; - document.getElementById("coastlineGroupName").style.display = "none"; - document.getElementById("coastlineGroupName").value = ""; - document.getElementById("coastlineGroup").style.display = "inline-block"; + document.querySelectorAll('#coastlineEditor > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('coastlineGroupsSelection').style.display = 'none'; + document.getElementById('coastlineGroupName').style.display = 'none'; + document.getElementById('coastlineGroupName').value = ''; + document.getElementById('coastlineGroup').style.display = 'inline-block'; } function selectCoastlineGroup(node) { const group = node.parentNode.id; - const select = document.getElementById("coastlineGroup"); + const select = document.getElementById('coastlineGroup'); select.options.length = 0; // remove all options - coastline.selectAll("g").each(function() { + coastline.selectAll('g').each(function () { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); } @@ -100,88 +122,89 @@ function editCoastline(node = d3.event.target) { } function toggleNewGroupInput() { - if (coastlineGroupName.style.display === "none") { - coastlineGroupName.style.display = "inline-block"; + if (coastlineGroupName.style.display === 'none') { + coastlineGroupName.style.display = 'inline-block'; coastlineGroupName.focus(); - coastlineGroup.style.display = "none"; + coastlineGroup.style.display = 'none'; } else { - coastlineGroupName.style.display = "none"; - coastlineGroup.style.display = "inline-block"; + coastlineGroupName.style.display = 'none'; + coastlineGroup.style.display = 'inline-block'; } } function createNewGroup() { - if (!this.value) {tip("Please provide a valid group name"); return;} - const group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (!this.value) { + tip('Please provide a valid group name'); + return; + } + const group = this.value + .toLowerCase() + .replace(/ /g, '_') + .replace(/[^\w\s]/gi, ''); if (document.getElementById(group)) { - tip("Element with this id already exists. Please provide a unique name", false, "error"); + tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } if (Number.isFinite(+group.charAt(0))) { - tip("Group name should start with a letter", false, "error"); + tip('Group name should start with a letter', false, 'error'); return; } // just rename if only 1 element left const oldGroup = elSelected.node().parentNode; - const basic = ["sea_island", "lake_island"].includes(oldGroup.id); + const basic = ['sea_island', 'lake_island'].includes(oldGroup.id); if (!basic && oldGroup.childElementCount === 1) { - document.getElementById("coastlineGroup").selectedOptions[0].remove(); - document.getElementById("coastlineGroup").options.add(new Option(group, group, false, true)); + document.getElementById('coastlineGroup').selectedOptions[0].remove(); + document.getElementById('coastlineGroup').options.add(new Option(group, group, false, true)); oldGroup.id = group; toggleNewGroupInput(); - document.getElementById("coastlineGroupName").value = ""; + document.getElementById('coastlineGroupName').value = ''; return; } // create a new group const newGroup = elSelected.node().parentNode.cloneNode(false); - document.getElementById("coastline").appendChild(newGroup); + document.getElementById('coastline').appendChild(newGroup); newGroup.id = group; - document.getElementById("coastlineGroup").options.add(new Option(group, group, false, true)); + document.getElementById('coastlineGroup').options.add(new Option(group, group, false, true)); document.getElementById(group).appendChild(elSelected.node()); toggleNewGroupInput(); - document.getElementById("coastlineGroupName").value = ""; + document.getElementById('coastlineGroupName').value = ''; } function removeCoastlineGroup() { const group = elSelected.node().parentNode.id; - if (["sea_island", "lake_island"].includes(group)) { - tip("This is one of the default groups, it cannot be removed", false, "error"); + if (['sea_island', 'lake_island'].includes(group)) { + tip('This is one of the default groups, it cannot be removed', false, 'error'); return; } const count = elSelected.node().parentNode.childElementCount; - alertMessage.innerHTML = `Are you sure you want to remove the group? - All coastline elements of the group (${count}) will be moved under sea_island group`; - $("#alert").dialog({resizable: false, title: "Remove coastline group", width:"26em", - buttons: { - Remove: function() { - $(this).dialog("close"); - const sea = document.getElementById("sea_island"); - const groupEl = document.getElementById(group); - while (groupEl.childNodes.length) { - sea.appendChild(groupEl.childNodes[0]); - } - groupEl.remove(); - document.getElementById("coastlineGroup").selectedOptions[0].remove(); - document.getElementById("coastlineGroup").value = "sea_island"; - }, - Cancel: function() {$(this).dialog("close");} + + const message = `Are you sure you want to remove the group?
All coastline elements of the group (${count}) will be moved under sea_island group`; + const onConfirm = () => { + const sea = document.getElementById('sea_island'); + const groupEl = document.getElementById(group); + while (groupEl.childNodes.length) { + sea.appendChild(groupEl.childNodes[0]); } - }); + groupEl.remove(); + document.getElementById('coastlineGroup').selectedOptions[0].remove(); + document.getElementById('coastlineGroup').value = 'sea_island'; + }; + confirmationDialog({title: 'Remove coastline group', message, confirm: 'Remove', onConfirm}); } function editGroupStyle() { const g = elSelected.node().parentNode.id; - editStyle("coastline", g); + editStyle('coastline', g); } function closeCoastlineEditor() { - debug.select("#vertices").remove(); + debug.select('#vertices').remove(); unselect(); } -} \ No newline at end of file +} diff --git a/modules/ui/cultures-editor.js b/modules/ui/cultures-editor.js index ecb1060e..90680098 100644 --- a/modules/ui/cultures-editor.js +++ b/modules/ui/cultures-editor.js @@ -1,39 +1,42 @@ -"use strict"; +'use strict'; function editCultures() { if (customization) return; - closeDialogs("#culturesEditor, .stable"); - if (!layerIsOn("toggleCultures")) toggleCultures(); - if (layerIsOn("toggleStates")) toggleStates(); - if (layerIsOn("toggleBiomes")) toggleBiomes(); - if (layerIsOn("toggleReligions")) toggleReligions(); - if (layerIsOn("toggleProvinces")) toggleProvinces(); + closeDialogs('#culturesEditor, .stable'); + if (!layerIsOn('toggleCultures')) toggleCultures(); + if (layerIsOn('toggleStates')) toggleStates(); + if (layerIsOn('toggleBiomes')) toggleBiomes(); + if (layerIsOn('toggleReligions')) toggleReligions(); + if (layerIsOn('toggleProvinces')) toggleProvinces(); - const body = document.getElementById("culturesBody"); + const body = document.getElementById('culturesBody'); drawCultureCenters(); refreshCulturesEditor(); if (modules.editCultures) return; modules.editCultures = true; - $("#culturesEditor").dialog({ - title: "Cultures Editor", resizable: false, width: fitContent(), close: closeCulturesEditor, - position: {my: "right top", at: "right-10 top+10", of: "svg"} + $('#culturesEditor').dialog({ + title: 'Cultures Editor', + resizable: false, + width: fitContent(), + close: closeCulturesEditor, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg'} }); body.focus(); // add listeners - document.getElementById("culturesEditorRefresh").addEventListener("click", refreshCulturesEditor); - document.getElementById("culturesEditStyle").addEventListener("click", () => editStyle("cults")); - document.getElementById("culturesLegend").addEventListener("click", toggleLegend); - document.getElementById("culturesPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("culturesHeirarchy").addEventListener("click", showHierarchy); - document.getElementById("culturesRecalculate").addEventListener("click", () => recalculateCultures(true)); - document.getElementById("culturesManually").addEventListener("click", enterCultureManualAssignent); - document.getElementById("culturesManuallyApply").addEventListener("click", applyCultureManualAssignent); - document.getElementById("culturesManuallyCancel").addEventListener("click", () => exitCulturesManualAssignment()); - document.getElementById("culturesEditNamesBase").addEventListener("click", editNamesbase); - document.getElementById("culturesAdd").addEventListener("click", enterAddCulturesMode); - document.getElementById("culturesExport").addEventListener("click", downloadCulturesData); + document.getElementById('culturesEditorRefresh').addEventListener('click', refreshCulturesEditor); + document.getElementById('culturesEditStyle').addEventListener('click', () => editStyle('cults')); + document.getElementById('culturesLegend').addEventListener('click', toggleLegend); + document.getElementById('culturesPercentage').addEventListener('click', togglePercentageMode); + document.getElementById('culturesHeirarchy').addEventListener('click', showHierarchy); + document.getElementById('culturesRecalculate').addEventListener('click', () => recalculateCultures(true)); + document.getElementById('culturesManually').addEventListener('click', enterCultureManualAssignent); + document.getElementById('culturesManuallyApply').addEventListener('click', applyCultureManualAssignent); + document.getElementById('culturesManuallyCancel').addEventListener('click', () => exitCulturesManualAssignment()); + document.getElementById('culturesEditNamesBase').addEventListener('click', editNamesbase); + document.getElementById('culturesAdd').addEventListener('click', enterAddCulturesMode); + document.getElementById('culturesExport').addEventListener('click', downloadCulturesData); function refreshCulturesEditor() { culturesCollectStatistics(); @@ -42,8 +45,9 @@ function editCultures() { } function culturesCollectStatistics() { - const cells = pack.cells, cultures = pack.cultures; - cultures.forEach(c => c.cells = c.area = c.rural = c.urban = 0); + const cells = pack.cells, + cultures = pack.cultures; + cultures.forEach((c) => (c.cells = c.area = c.rural = c.urban = 0)); for (const i of cells.i) { if (cells.h[i] < 20) continue; @@ -57,15 +61,17 @@ function editCultures() { // add line for each culture function culturesEditorAddLines() { - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - let lines = "", totalArea = 0, totalPopulation = 0; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + let lines = '', + totalArea = 0, + totalPopulation = 0; - const emblemShapeGroup = document.getElementById("emblemShape").selectedOptions[0].parentNode.label; - const selectShape = emblemShapeGroup === "Diversiform"; + const emblemShapeGroup = document.getElementById('emblemShape').selectedOptions[0].parentNode.label; + const selectShape = emblemShapeGroup === 'Diversiform'; for (const c of pack.cultures) { if (c.removed) continue; - const area = c.area * (distanceScaleInput.value ** 2); + const area = c.area * distanceScaleInput.value ** 2; const rural = c.rural * populationRate.value; const urban = c.urban * populationRate.value * urbanization.value; const population = rn(rural + urban); @@ -90,19 +96,23 @@ function editCultures() {
${si(population)}
- ${selectShape ? `` : ""} + ${selectShape ? `` : ''} `; continue; } lines += `
- +
${c.cells}
- +
${si(area) + unit}
@@ -110,104 +120,134 @@ function editCultures() {
${si(population)}
- ${selectShape ? `` : ""} + ${selectShape ? `` : ''}
`; } body.innerHTML = lines; // update footer - culturesFooterCultures.innerHTML = pack.cultures.filter(c => c.i && !c.removed).length; - culturesFooterCells.innerHTML = pack.cells.h.filter(h => h >= 20).length; + culturesFooterCultures.innerHTML = pack.cultures.filter((c) => c.i && !c.removed).length; + culturesFooterCells.innerHTML = pack.cells.h.filter((h) => h >= 20).length; culturesFooterArea.innerHTML = si(totalArea) + unit; culturesFooterPopulation.innerHTML = si(totalPopulation); culturesFooterArea.dataset.area = totalArea; culturesFooterPopulation.dataset.population = totalPopulation; // add listeners - body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseenter", ev => cultureHighlightOn(ev))); - body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseleave", ev => cultureHighlightOff(ev))); - body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectCultureOnLineClick)); - body.querySelectorAll("rect.fillRect").forEach(el => el.addEventListener("click", cultureChangeColor)); - body.querySelectorAll("div > input.cultureName").forEach(el => el.addEventListener("input", cultureChangeName)); - body.querySelectorAll("div > input.statePower").forEach(el => el.addEventListener("input", cultureChangeExpansionism)); - body.querySelectorAll("div > select.cultureType").forEach(el => el.addEventListener("change", cultureChangeType)); - body.querySelectorAll("div > select.cultureBase").forEach(el => el.addEventListener("change", cultureChangeBase)); - body.querySelectorAll("div > select.cultureShape").forEach(el => el.addEventListener("change", cultureChangeShape)); - body.querySelectorAll("div > div.culturePopulation").forEach(el => el.addEventListener("click", changePopulation)); - body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.addEventListener("click", cultureRegenerateBurgs)); - body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", cultureRemove)); + body.querySelectorAll('div.cultures').forEach((el) => el.addEventListener('mouseenter', (ev) => cultureHighlightOn(ev))); + body.querySelectorAll('div.cultures').forEach((el) => el.addEventListener('mouseleave', (ev) => cultureHighlightOff(ev))); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('click', selectCultureOnLineClick)); + body.querySelectorAll('rect.fillRect').forEach((el) => el.addEventListener('click', cultureChangeColor)); + body.querySelectorAll('div > input.cultureName').forEach((el) => el.addEventListener('input', cultureChangeName)); + body.querySelectorAll('div > input.statePower').forEach((el) => el.addEventListener('input', cultureChangeExpansionism)); + body.querySelectorAll('div > select.cultureType').forEach((el) => el.addEventListener('change', cultureChangeType)); + body.querySelectorAll('div > select.cultureBase').forEach((el) => el.addEventListener('change', cultureChangeBase)); + body.querySelectorAll('div > select.cultureShape').forEach((el) => el.addEventListener('change', cultureChangeShape)); + body.querySelectorAll('div > div.culturePopulation').forEach((el) => el.addEventListener('click', changePopulation)); + body.querySelectorAll('div > span.icon-arrows-cw').forEach((el) => el.addEventListener('click', cultureRegenerateBurgs)); + body.querySelectorAll('div > span.icon-trash-empty').forEach((el) => el.addEventListener('click', cultureRemove)); - culturesHeader.querySelector("div[data-sortby='emblems']").style.display = selectShape ? "inline-block" : "none"; + culturesHeader.querySelector("div[data-sortby='emblems']").style.display = selectShape ? 'inline-block' : 'none'; - if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();} + if (body.dataset.type === 'percentage') { + body.dataset.type = 'absolute'; + togglePercentageMode(); + } applySorting(culturesHeader); - $("#culturesEditor").dialog({width: fitContent()}); + $('#culturesEditor').dialog({width: fitContent()}); } function getTypeOptions(type) { - let options = ""; - const types = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; - types.forEach(t => options += ``); + let options = ''; + const types = ['Generic', 'River', 'Lake', 'Naval', 'Nomadic', 'Hunting', 'Highland']; + types.forEach((t) => (options += ``)); return options; } function getBaseOptions(base) { - let options = ""; - nameBases.forEach((n, i) => options += ``); + let options = ''; + nameBases.forEach((n, i) => (options += ``)); return options; } function getShapeOptions(selected) { - const shapes = Object.keys(COA.shields.types).map(type => Object.keys(COA.shields[type])).flat(); - return shapes.map(shape => ``); + const shapes = Object.keys(COA.shields.types) + .map((type) => Object.keys(COA.shields[type])) + .flat(); + return shapes.map((shape) => ``); } function cultureHighlightOn(event) { const culture = +event.target.dataset.id; - const info = document.getElementById("cultureInfo"); + const info = document.getElementById('cultureInfo'); if (info) { - d3.select("#hierarchy").select("g[data-id='"+culture+"'] > path").classed("selected", 1); + d3.select('#hierarchy') + .select("g[data-id='" + culture + "'] > path") + .classed('selected', 1); const c = pack.cultures[culture]; const rural = c.rural * populationRate.value; const urban = c.urban * populationRate.value * urbanization.value; - const population = rural + urban > 0 ? si(rn(rural + urban)) + " people" : "Extinct"; + const population = rural + urban > 0 ? si(rn(rural + urban)) + ' people' : 'Extinct'; info.innerHTML = `${c.name} culture. ${c.type}. ${population}`; - tip("Drag to change parent, drag to itself to move to the top level. Hold CTRL and click to change abbreviation"); + tip('Drag to change parent, drag to itself to move to the top level. Hold CTRL and click to change abbreviation'); } - if (!layerIsOn("toggleCultures")) return; + if (!layerIsOn('toggleCultures')) return; if (customization) return; const animate = d3.transition().duration(2000).ease(d3.easeSinIn); - cults.select("#culture"+culture).raise().transition(animate).attr("stroke-width", 2.5).attr("stroke", "#d0240f"); - debug.select("#cultureCenter"+culture).raise().transition(animate).attr("r", 8).attr("stroke", "#d0240f"); + cults + .select('#culture' + culture) + .raise() + .transition(animate) + .attr('stroke-width', 2.5) + .attr('stroke', '#d0240f'); + debug + .select('#cultureCenter' + culture) + .raise() + .transition(animate) + .attr('r', 8) + .attr('stroke', '#d0240f'); } function cultureHighlightOff(event) { const culture = +event.target.dataset.id; - const info = document.getElementById("cultureInfo"); + const info = document.getElementById('cultureInfo'); if (info) { - d3.select("#hierarchy").select("g[data-id='"+culture+"'] > path").classed("selected", 0); - info.innerHTML = "‍"; - tip(""); + d3.select('#hierarchy') + .select("g[data-id='" + culture + "'] > path") + .classed('selected', 0); + info.innerHTML = '‍'; + tip(''); } - if (!layerIsOn("toggleCultures")) return; - cults.select("#culture"+culture).transition().attr("stroke-width", null).attr("stroke", null); - debug.select("#cultureCenter"+culture).transition().attr("r", 6).attr("stroke", null); + if (!layerIsOn('toggleCultures')) return; + cults + .select('#culture' + culture) + .transition() + .attr('stroke-width', null) + .attr('stroke', null); + debug + .select('#cultureCenter' + culture) + .transition() + .attr('r', 6) + .attr('stroke', null); } function cultureChangeColor() { const el = this; - const currentFill = el.getAttribute("fill"); + const currentFill = el.getAttribute('fill'); const culture = +el.parentNode.parentNode.dataset.id; - const callback = function(fill) { - el.setAttribute("fill", fill); + const callback = function (fill) { + el.setAttribute('fill', fill); pack.cultures[culture].color = fill; - cults.select("#culture"+culture).attr("fill", fill).attr("stroke", fill); - debug.select("#cultureCenter"+culture).attr("fill", fill); - } + cults + .select('#culture' + culture) + .attr('fill', fill) + .attr('stroke', fill); + debug.select('#cultureCenter' + culture).attr('fill', fill); + }; openPicker(currentFill, callback); } @@ -216,7 +256,10 @@ function editCultures() { const culture = +this.parentNode.dataset.id; this.parentNode.dataset.name = this.value; pack.cultures[culture].name = this.value; - pack.cultures[culture].code = abbreviate(this.value, pack.cultures.map(c => c.code)); + pack.cultures[culture].code = abbreviate( + this.value, + pack.cultures.map((c) => c.code) + ); } function cultureChangeExpansionism() { @@ -249,148 +292,174 @@ function editCultures() { if (!coaEl) return; // not rendered coaEl.remove(); COArenderer.trigger(id, coa); - } + }; - pack.states.forEach(state => { - if (state.culture !== culture || !state.i || state.removed || !state.coa || state.coa === "custom") return; + pack.states.forEach((state) => { + if (state.culture !== culture || !state.i || state.removed || !state.coa || state.coa === 'custom') return; if (shape === state.coa.shield) return; state.coa.shield = shape; - rerenderCOA("stateCOA" + state.i, state.coa); + rerenderCOA('stateCOA' + state.i, state.coa); }); - pack.provinces.forEach(province => { - if (pack.cells.culture[province.center] !== culture || !province.i || province.removed || !province.coa || province.coa === "custom") return; + pack.provinces.forEach((province) => { + if (pack.cells.culture[province.center] !== culture || !province.i || province.removed || !province.coa || province.coa === 'custom') return; if (shape === province.coa.shield) return; province.coa.shield = shape; - rerenderCOA("provinceCOA" + province.i, province.coa); + rerenderCOA('provinceCOA' + province.i, province.coa); }); - pack.burgs.forEach(burg => { - if (burg.culture !== culture || !burg.i || burg.removed || !burg.coa || burg.coa === "custom") return; + pack.burgs.forEach((burg) => { + if (burg.culture !== culture || !burg.i || burg.removed || !burg.coa || burg.coa === 'custom') return; if (shape === burg.coa.shield) return; - burg.coa.shield = shape - rerenderCOA("burgCOA" + burg.i, burg.coa); + burg.coa.shield = shape; + rerenderCOA('burgCOA' + burg.i, burg.coa); }); } function changePopulation() { const culture = +this.parentNode.dataset.id; const c = pack.cultures[culture]; - if (!c.cells) {tip("Culture does not have any cells, cannot change population", false, "error"); return;} + if (!c.cells) { + tip('Culture does not have any cells, cannot change population', false, 'error'); + return; + } const rural = rn(c.rural * populationRate.value); const urban = rn(c.urban * populationRate.value * urbanization.value); const total = rural + urban; - const l = n => Number(n).toLocaleString(); - const burgs = pack.burgs.filter(b => !b.removed && b.culture === culture); + const l = (n) => Number(n).toLocaleString(); + const burgs = pack.burgs.filter((b) => !b.removed && b.culture === culture); alertMessage.innerHTML = ` Rural: - Urban: + Urban:

Total population: ${l(total)} ⇒ ${l(total)} (100%)

`; - const update = function() { + const update = function () { const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber; if (isNaN(totalNew)) return; totalPop.innerHTML = l(totalNew); - totalPopPerc.innerHTML = rn(totalNew / total * 100); - } + totalPopPerc.innerHTML = rn((totalNew / total) * 100); + }; ruralPop.oninput = () => update(); urbanPop.oninput = () => update(); - $("#alert").dialog({ - resizable: false, title: "Change culture population", width: "24em", buttons: { - Apply: function() {applyPopulationChange(); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");} - }, position: {my: "center", at: "center", of: "svg"} + $('#alert').dialog({ + resizable: false, + title: 'Change culture population', + width: '24em', + buttons: { + Apply: function () { + applyPopulationChange(); + $(this).dialog('close'); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); function applyPopulationChange() { const ruralChange = ruralPop.value / rural; if (isFinite(ruralChange) && ruralChange !== 1) { - const cells = pack.cells.i.filter(i => pack.cells.culture[i] === culture); - cells.forEach(i => pack.cells.pop[i] *= ruralChange); + const cells = pack.cells.i.filter((i) => pack.cells.culture[i] === culture); + cells.forEach((i) => (pack.cells.pop[i] *= ruralChange)); } if (!isFinite(ruralChange) && +ruralPop.value > 0) { const points = ruralPop.value / populationRate.value; - const cells = pack.cells.i.filter(i => pack.cells.culture[i] === culture); + const cells = pack.cells.i.filter((i) => pack.cells.culture[i] === culture); const pop = rn(points / cells.length); - cells.forEach(i => pack.cells.pop[i] = pop); + cells.forEach((i) => (pack.cells.pop[i] = pop)); } const urbanChange = urbanPop.value / urban; if (isFinite(urbanChange) && urbanChange !== 1) { - burgs.forEach(b => b.population = rn(b.population * urbanChange, 4)); + burgs.forEach((b) => (b.population = rn(b.population * urbanChange, 4))); } if (!isFinite(urbanChange) && +urbanPop.value > 0) { const points = urbanPop.value / populationRate.value / urbanization.value; const population = rn(points / burgs.length, 4); - burgs.forEach(b => b.population = population); + burgs.forEach((b) => (b.population = population)); } refreshCulturesEditor(); } - } function cultureRegenerateBurgs() { if (customization === 4) return; const culture = +this.parentNode.dataset.id; - const cBurgs = pack.burgs.filter(b => b.culture === culture && !b.lock); - cBurgs.forEach(b => { + const cBurgs = pack.burgs.filter((b) => b.culture === culture && !b.lock); + cBurgs.forEach((b) => { b.name = Names.getCulture(culture); - labels.select("[data-id='" + b.i +"']").text(b.name); + labels.select("[data-id='" + b.i + "']").text(b.name); }); - tip(`Names for ${cBurgs.length} burgs are regenerated`, false, "success"); + tip(`Names for ${cBurgs.length} burgs are regenerated`, false, 'success'); } function cultureRemove() { if (customization === 4) return; const culture = +this.parentNode.dataset.id; - alertMessage.innerHTML = "Are you sure you want to remove the culture?
This action cannot be reverted"; - $("#alert").dialog({resizable: false, title: "Remove culture", - buttons: { - Remove: function() { - cults.select("#culture"+culture).remove(); - debug.select("#cultureCenter"+culture).remove(); + const message = 'Are you sure you want to remove the culture?
This action cannot be reverted'; + const onConfirm = () => { + cults.select('#culture' + culture).remove(); + debug.select('#cultureCenter' + culture).remove(); - pack.burgs.filter(b => b.culture == culture).forEach(b => b.culture = 0); - pack.states.forEach((s, i) => {if(s.culture === culture) s.culture = 0;}); - pack.cells.culture.forEach((c, i) => {if(c === culture) pack.cells.culture[i] = 0;}); - pack.cultures[culture].removed = true; + pack.burgs.filter((b) => b.culture == culture).forEach((b) => (b.culture = 0)); + pack.states.forEach((s, i) => { + if (s.culture === culture) s.culture = 0; + }); + pack.cells.culture.forEach((c, i) => { + if (c === culture) pack.cells.culture[i] = 0; + }); + pack.cultures[culture].removed = true; - const origin = pack.cultures[culture].origin; - pack.cultures.forEach(c => {if(c.origin === culture) c.origin = origin;}); - refreshCulturesEditor(); - $(this).dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const origin = pack.cultures[culture].origin; + pack.cultures.forEach((c) => { + if (c.origin === culture) c.origin = origin; + }); + refreshCulturesEditor(); + }; + confirmationDialog({title: 'Remove culture', message, confirm: 'Remove', onConfirm}); } function drawCultureCenters() { const tooltip = 'Drag to move the culture center (ancestral home)'; - debug.select("#cultureCenters").remove(); - const cultureCenters = debug.append("g").attr("id", "cultureCenters") - .attr("stroke-width", 2).attr("stroke", "#444444").style("cursor", "move"); + debug.select('#cultureCenters').remove(); + const cultureCenters = debug.append('g').attr('id', 'cultureCenters').attr('stroke-width', 2).attr('stroke', '#444444').style('cursor', 'move'); - const data = pack.cultures.filter(c => c.i && !c.removed); - cultureCenters.selectAll("circle").data(data).enter().append("circle") - .attr("id", d => "cultureCenter"+d.i).attr("data-id", d => d.i) - .attr("r", 6).attr("fill", d => d.color) - .attr("cx", d => pack.cells.p[d.center][0]).attr("cy", d => pack.cells.p[d.center][1]) - .on("mouseenter", d => {tip(tooltip, true); body.querySelector(`div[data-id='${d.i}']`).classList.add("selected"); cultureHighlightOn(event);}) - .on("mouseleave", d => {tip('', true); body.querySelector(`div[data-id='${d.i}']`).classList.remove("selected"); cultureHighlightOff(event);}) - .call(d3.drag().on("start", cultureCenterDrag)); + const data = pack.cultures.filter((c) => c.i && !c.removed); + cultureCenters + .selectAll('circle') + .data(data) + .enter() + .append('circle') + .attr('id', (d) => 'cultureCenter' + d.i) + .attr('data-id', (d) => d.i) + .attr('r', 6) + .attr('fill', (d) => d.color) + .attr('cx', (d) => pack.cells.p[d.center][0]) + .attr('cy', (d) => pack.cells.p[d.center][1]) + .on('mouseenter', (d) => { + tip(tooltip, true); + body.querySelector(`div[data-id='${d.i}']`).classList.add('selected'); + cultureHighlightOn(event); + }) + .on('mouseleave', (d) => { + tip('', true); + body.querySelector(`div[data-id='${d.i}']`).classList.remove('selected'); + cultureHighlightOff(event); + }) + .call(d3.drag().on('start', cultureCenterDrag)); } function cultureCenterDrag() { const el = d3.select(this); const c = +this.id.slice(13); - d3.event.on("drag", () => { - el.attr("cx", d3.event.x).attr("cy", d3.event.y); + d3.event.on('drag', () => { + el.attr('cx', d3.event.x).attr('cy', d3.event.y); const cell = findCell(d3.event.x, d3.event.y); if (pack.cells.h[cell] < 20) return; // ignore dragging on water pack.cultures[c].center = cell; @@ -399,25 +468,31 @@ function editCultures() { } function toggleLegend() { - if (legend.selectAll("*").size()) {clearLegend(); return;}; // hide legend - const data = pack.cultures.filter(c => c.i && !c.removed && c.cells).sort((a, b) => b.area - a.area).map(c => [c.i, c.color, c.name]); - drawLegend("Cultures", data); + if (legend.selectAll('*').size()) { + clearLegend(); + return; + } // hide legend + const data = pack.cultures + .filter((c) => c.i && !c.removed && c.cells) + .sort((a, b) => b.area - a.area) + .map((c) => [c.i, c.color, c.name]); + drawLegend('Cultures', data); } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; + if (body.dataset.type === 'absolute') { + body.dataset.type = 'percentage'; const totalCells = +culturesFooterCells.innerHTML; const totalArea = +culturesFooterArea.dataset.area; const totalPopulation = +culturesFooterPopulation.dataset.population; - body.querySelectorAll(":scope > div").forEach(function(el) { - el.querySelector(".stateCells").innerHTML = rn(+el.dataset.cells / totalCells * 100) + "%"; - el.querySelector(".biomeArea").innerHTML = rn(+el.dataset.area / totalArea * 100) + "%"; - el.querySelector(".culturePopulation").innerHTML = rn(+el.dataset.population / totalPopulation * 100) + "%"; + body.querySelectorAll(':scope > div').forEach(function (el) { + el.querySelector('.stateCells').innerHTML = rn((+el.dataset.cells / totalCells) * 100) + '%'; + el.querySelector('.biomeArea').innerHTML = rn((+el.dataset.area / totalArea) * 100) + '%'; + el.querySelector('.culturePopulation').innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + '%'; }); } else { - body.dataset.type = "absolute"; + body.dataset.type = 'absolute'; culturesEditorAddLines(); } } @@ -425,12 +500,19 @@ function editCultures() { function showHierarchy() { // build hierarchy tree pack.cultures[0].origin = null; - const cultures = pack.cultures.filter(c => !c.removed); - if (cultures.length < 3) {tip("Not enough cultures to show hierarchy", false, "error"); return;} - const root = d3.stratify().id(d => d.i).parentId(d => d.origin)(cultures); + const cultures = pack.cultures.filter((c) => !c.removed); + if (cultures.length < 3) { + tip('Not enough cultures to show hierarchy', false, 'error'); + return; + } + const root = d3 + .stratify() + .id((d) => d.i) + .parentId((d) => d.origin)(cultures); const treeWidth = root.leaves().length; const treeHeight = root.height; - const width = treeWidth * 40, height = treeHeight * 60; + const width = treeWidth * 40, + height = treeHeight * 60; const margin = {top: 10, right: 10, bottom: -5, left: 10}; const w = width - margin.left - margin.right; @@ -439,79 +521,123 @@ function editCultures() { // prepare svg alertMessage.innerHTML = "
"; - const svg = d3.select("#alertMessage").insert("svg", "#cultureInfo").attr("id", "hierarchy") - .attr("width", width).attr("height", height).style("text-anchor", "middle"); - const graph = svg.append("g").attr("transform", `translate(10, -45)`); - const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa"); - const nodes = graph.append("g"); + const svg = d3.select('#alertMessage').insert('svg', '#cultureInfo').attr('id', 'hierarchy').attr('width', width).attr('height', height).style('text-anchor', 'middle'); + const graph = svg.append('g').attr('transform', `translate(10, -45)`); + const links = graph.append('g').attr('fill', 'none').attr('stroke', '#aaaaaa'); + const nodes = graph.append('g'); renderTree(); function renderTree() { treeLayout(root); - links.selectAll('path').data(root.links()).enter() - .append('path').attr("d", d => {return "M" + d.source.x + "," + d.source.y - + "C" + d.source.x + "," + (d.source.y * 3 + d.target.y) / 4 - + " " + d.target.x + "," + (d.source.y * 2 + d.target.y) / 3 - + " " + d.target.x + "," + d.target.y;}); + links + .selectAll('path') + .data(root.links()) + .enter() + .append('path') + .attr('d', (d) => { + return ( + 'M' + + d.source.x + + ',' + + d.source.y + + 'C' + + d.source.x + + ',' + + (d.source.y * 3 + d.target.y) / 4 + + ' ' + + d.target.x + + ',' + + (d.source.y * 2 + d.target.y) / 3 + + ' ' + + d.target.x + + ',' + + d.target.y + ); + }); - const node = nodes.selectAll('g').data(root.descendants()).enter() - .append('g').attr("data-id", d => d.data.i).attr("stroke", "#333333") - .attr("transform", d => `translate(${d.x}, ${d.y})`) - .on("mouseenter", () => cultureHighlightOn(event)) - .on("mouseleave", () => cultureHighlightOff(event)) - .call(d3.drag().on("start", d => dragToReorigin(d))); + const node = nodes + .selectAll('g') + .data(root.descendants()) + .enter() + .append('g') + .attr('data-id', (d) => d.data.i) + .attr('stroke', '#333333') + .attr('transform', (d) => `translate(${d.x}, ${d.y})`) + .on('mouseenter', () => cultureHighlightOn(event)) + .on('mouseleave', () => cultureHighlightOff(event)) + .call(d3.drag().on('start', (d) => dragToReorigin(d))); - node.append("path").attr("d", d => { - if (!d.data.i) return "M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0"; else // small circle - if (d.data.type === "Generic") return "M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0"; else // circle - if (d.data.type === "River") return "M0,-14L14,0L0,14L-14,0Z"; else // diamond - if (d.data.type === "Lake") return "M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z"; else // hexagon - if (d.data.type === "Naval") return "M-11,-11h22v22h-22Z"; // square - if (d.data.type === "Highland") return "M-11,-11l11,2l11,-2l-2,11l2,11l-11,-2l-11,2l2,-11Z"; // concave square - if (d.data.type === "Nomadic") return "M-4.97,-12.01 l9.95,0 l7.04,7.04 l0,9.95 l-7.04,7.04 l-9.95,0 l-7.04,-7.04 l0,-9.95Z"; // octagon - if (d.data.type === "Hunting") return "M0,-14l14,11l-6,14h-16l-6,-14Z"; // pentagon - return "M-11,-11h22v22h-22Z"; // square - }).attr("fill", d => d.data.i ? d.data.color : "#ffffff") - .attr("stroke-dasharray", d => d.data.cells ? "null" : "1"); + node + .append('path') + .attr('d', (d) => { + if (!d.data.i) return 'M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0'; + // small circle + else if (d.data.type === 'Generic') return 'M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0'; + // circle + else if (d.data.type === 'River') return 'M0,-14L14,0L0,14L-14,0Z'; + // diamond + else if (d.data.type === 'Lake') return 'M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z'; + // hexagon + else if (d.data.type === 'Naval') return 'M-11,-11h22v22h-22Z'; // square + if (d.data.type === 'Highland') return 'M-11,-11l11,2l11,-2l-2,11l2,11l-11,-2l-11,2l2,-11Z'; // concave square + if (d.data.type === 'Nomadic') return 'M-4.97,-12.01 l9.95,0 l7.04,7.04 l0,9.95 l-7.04,7.04 l-9.95,0 l-7.04,-7.04 l0,-9.95Z'; // octagon + if (d.data.type === 'Hunting') return 'M0,-14l14,11l-6,14h-16l-6,-14Z'; // pentagon + return 'M-11,-11h22v22h-22Z'; // square + }) + .attr('fill', (d) => (d.data.i ? d.data.color : '#ffffff')) + .attr('stroke-dasharray', (d) => (d.data.cells ? 'null' : '1')); - node.append("text").attr("dy", ".35em").text(d => d.data.i ? d.data.code : ''); + node + .append('text') + .attr('dy', '.35em') + .text((d) => (d.data.i ? d.data.code : '')); } - $("#alert").dialog({ - title: "Cultures tree", width: fitContent(), resizable: false, - position: {my: "left center", at: "left+10 center", of: "svg"}, buttons: {}, - close: () => {alertMessage.innerHTML = "";} + $('#alert').dialog({ + title: 'Cultures tree', + width: fitContent(), + resizable: false, + position: {my: 'left center', at: 'left+10 center', of: 'svg'}, + buttons: {}, + close: () => { + alertMessage.innerHTML = ''; + } }); function dragToReorigin(d) { - if (isCtrlClick(d3.event.sourceEvent)) {changeCode(d); return;} + if (isCtrlClick(d3.event.sourceEvent)) { + changeCode(d); + return; + } - const originLine = graph.append("path") - .attr("class", "dragLine").attr("d", `M${d.x},${d.y}L${d.x},${d.y}`); + const originLine = graph.append('path').attr('class', 'dragLine').attr('d', `M${d.x},${d.y}L${d.x},${d.y}`); - d3.event.on("drag", () => { - originLine.attr("d", `M${d.x},${d.y}L${d3.event.x},${d3.event.y}`) + d3.event.on('drag', () => { + originLine.attr('d', `M${d.x},${d.y}L${d3.event.x},${d3.event.y}`); }); - d3.event.on("end", () => { + d3.event.on('end', () => { originLine.remove(); - const selected = graph.select("path.selected"); + const selected = graph.select('path.selected'); if (!selected.size()) return; const culture = d.data.i; const oldOrigin = d.data.origin; let newOrigin = selected.datum().data.i; if (newOrigin == oldOrigin) return; // already a child of the selected node if (newOrigin == culture) newOrigin = 0; // move to top - if (newOrigin && d.descendants().some(node => node.id == newOrigin)) return; // cannot be a child of its own child + if (newOrigin && d.descendants().some((node) => node.id == newOrigin)) return; // cannot be a child of its own child pack.cultures[culture].origin = d.data.origin = newOrigin; // change data - showHierarchy() // update hierarchy + showHierarchy(); // update hierarchy }); } function changeCode(d) { - prompt(`Please provide an abbreviation for culture: ${d.data.name}`, {default:d.data.code}, v => { + prompt(`Please provide an abbreviation for culture: ${d.data.name}`, {default: d.data.code}, (v) => { pack.cultures[d.data.i].code = v; - nodes.select("g[data-id='"+d.data.i+"']").select("text").text(v); + nodes + .select("g[data-id='" + d.data.i + "']") + .select('text') + .text(v); }); } } @@ -520,45 +646,42 @@ function editCultures() { if (!must && !culturesAutoChange.checked) return; pack.cells.culture = new Uint16Array(pack.cells.i.length); - pack.cultures.forEach(function(c) { + pack.cultures.forEach(function (c) { if (!c.i || c.removed) return; pack.cells.culture[c.center] = c.i; }); Cultures.expand(); drawCultures(); - pack.burgs.forEach(b => b.culture = pack.cells.culture[b.cell]); + pack.burgs.forEach((b) => (b.culture = pack.cells.culture[b.cell])); refreshCulturesEditor(); - document.querySelector("input.statePower").focus(); // to not trigger hotkeys + document.querySelector('input.statePower').focus(); // to not trigger hotkeys } function enterCultureManualAssignent() { - if (!layerIsOn("toggleCultures")) toggleCultures(); + if (!layerIsOn('toggleCultures')) toggleCultures(); customization = 4; - cults.append("g").attr("id", "temp"); - document.querySelectorAll("#culturesBottom > *").forEach(el => el.style.display = "none"); - document.getElementById("culturesManuallyButtons").style.display = "inline-block"; - debug.select("#cultureCenters").style("display", "none"); + cults.append('g').attr('id', 'temp'); + document.querySelectorAll('#culturesBottom > *').forEach((el) => (el.style.display = 'none')); + document.getElementById('culturesManuallyButtons').style.display = 'inline-block'; + debug.select('#cultureCenters').style('display', 'none'); - culturesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); - culturesHeader.querySelector("div[data-sortby='type']").style.left = "8.8em"; - culturesHeader.querySelector("div[data-sortby='base']").style.left = "13.6em"; - culturesFooter.style.display = "none"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); - $("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}}); + culturesEditor.querySelectorAll('.hide').forEach((el) => el.classList.add('hidden')); + culturesHeader.querySelector("div[data-sortby='type']").style.left = '8.8em'; + culturesHeader.querySelector("div[data-sortby='base']").style.left = '13.6em'; + culturesFooter.style.display = 'none'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); + $('#culturesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg'}}); - tip("Click on culture to select, drag the circle to change culture", true); - viewbox.style("cursor", "crosshair") - .on("click", selectCultureOnMapClick) - .call(d3.drag().on("start", dragCultureBrush)) - .on("touchmove mousemove", moveCultureBrush); + tip('Click on culture to select, drag the circle to change culture', true); + viewbox.style('cursor', 'crosshair').on('click', selectCultureOnMapClick).call(d3.drag().on('start', dragCultureBrush)).on('touchmove mousemove', moveCultureBrush); - body.querySelector("div").classList.add("selected"); + body.querySelector('div').classList.add('selected'); } function selectCultureOnLineClick(i) { if (customization !== 4) return; - body.querySelector("div.selected").classList.remove("selected"); - this.classList.add("selected"); + body.querySelector('div.selected').classList.remove('selected'); + this.classList.add('selected'); } function selectCultureOnMapClick() { @@ -566,17 +689,17 @@ function editCultures() { const i = findCell(point[0], point[1]); if (pack.cells.h[i] < 20) return; - const assigned = cults.select("#temp").select("polygon[data-cell='"+i+"']"); - const culture = assigned.size() ? +assigned.attr("data-culture") : pack.cells.culture[i]; + const assigned = cults.select('#temp').select("polygon[data-cell='" + i + "']"); + const culture = assigned.size() ? +assigned.attr('data-culture') : pack.cells.culture[i]; - body.querySelector("div.selected").classList.remove("selected"); - body.querySelector("div[data-id='"+culture+"']").classList.add("selected"); + body.querySelector('div.selected').classList.remove('selected'); + body.querySelector("div[data-id='" + culture + "']").classList.add('selected'); } function dragCultureBrush() { const r = +culturesManuallyBrush.value; - d3.event.on("drag", () => { + d3.event.on('drag', () => { if (!d3.event.dx && !d3.event.dy) return; const p = d3.mouse(this); moveCircle(p[0], p[1], r); @@ -588,20 +711,20 @@ function editCultures() { } function changeCultureForSelection(selection) { - const temp = cults.select("#temp"); - const selected = body.querySelector("div.selected"); + const temp = cults.select('#temp'); + const selected = body.querySelector('div.selected'); const cultureNew = +selected.dataset.id; - const color = pack.cultures[cultureNew].color || "#ffffff"; + const color = pack.cultures[cultureNew].color || '#ffffff'; - selection.forEach(function(i) { - const exists = temp.select("polygon[data-cell='"+i+"']"); - const cultureOld = exists.size() ? +exists.attr("data-culture") : pack.cells.culture[i]; + selection.forEach(function (i) { + const exists = temp.select("polygon[data-cell='" + i + "']"); + const cultureOld = exists.size() ? +exists.attr('data-culture') : pack.cells.culture[i]; if (cultureNew === cultureOld) return; // change of append new element - if (exists.size()) exists.attr("data-culture", cultureNew).attr("fill", color).attr("stroke", color); - else temp.append("polygon").attr("data-cell", i).attr("data-culture", cultureNew).attr("points", getPackPolygon(i)).attr("fill", color).attr("stroke", color); + if (exists.size()) exists.attr('data-culture', cultureNew).attr('fill', color).attr('stroke', color); + else temp.append('polygon').attr('data-cell', i).attr('data-culture', cultureNew).attr('points', getPackPolygon(i)).attr('fill', color).attr('stroke', color); }); } @@ -613,8 +736,8 @@ function editCultures() { } function applyCultureManualAssignent() { - const changed = cults.select("#temp").selectAll("polygon"); - changed.each(function() { + const changed = cults.select('#temp').selectAll('polygon'); + changed.each(function () { const i = +this.dataset.cell; const c = +this.dataset.culture; pack.cells.culture[i] = c; @@ -630,48 +753,57 @@ function editCultures() { function exitCulturesManualAssignment(close) { customization = 0; - cults.select("#temp").remove(); + cults.select('#temp').remove(); removeCircle(); - document.querySelectorAll("#culturesBottom > *").forEach(el => el.style.display = "inline-block"); - document.getElementById("culturesManuallyButtons").style.display = "none"; + document.querySelectorAll('#culturesBottom > *').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('culturesManuallyButtons').style.display = 'none'; - culturesEditor.querySelectorAll(".hide").forEach(el => el.classList.remove("hidden")); - culturesHeader.querySelector("div[data-sortby='type']").style.left = "18.6em"; - culturesHeader.querySelector("div[data-sortby='base']").style.left = "35.8em"; - culturesFooter.style.display = "block"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if(!close) $("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}}); + culturesEditor.querySelectorAll('.hide').forEach((el) => el.classList.remove('hidden')); + culturesHeader.querySelector("div[data-sortby='type']").style.left = '18.6em'; + culturesHeader.querySelector("div[data-sortby='base']").style.left = '35.8em'; + culturesFooter.style.display = 'block'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (!close) $('#culturesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg'}}); - debug.select("#cultureCenters").style("display", null); + debug.select('#cultureCenters').style('display', null); restoreDefaultEvents(); clearMainTip(); - const selected = body.querySelector("div.selected"); - if (selected) selected.classList.remove("selected"); + const selected = body.querySelector('div.selected'); + if (selected) selected.classList.remove('selected'); } function enterAddCulturesMode() { - if (this.classList.contains("pressed")) {exitAddCultureMode(); return;}; + if (this.classList.contains('pressed')) { + exitAddCultureMode(); + return; + } customization = 9; - this.classList.add("pressed"); - tip("Click on the map to add a new culture", true); - viewbox.style("cursor", "crosshair").on("click", addCulture); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); + this.classList.add('pressed'); + tip('Click on the map to add a new culture', true); + viewbox.style('cursor', 'crosshair').on('click', addCulture); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); } function exitAddCultureMode() { customization = 0; restoreDefaultEvents(); clearMainTip(); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if (culturesAdd.classList.contains("pressed")) culturesAdd.classList.remove("pressed"); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (culturesAdd.classList.contains('pressed')) culturesAdd.classList.remove('pressed'); } function addCulture() { const point = d3.mouse(this); const center = findCell(point[0], point[1]); - if (pack.cells.h[center] < 20) {tip("You cannot place culture center into the water. Please click on a land cell", false, "error"); return;} - const occupied = pack.cultures.some(c => !c.removed && c.center === center); - if (occupied) {tip("This cell is already a culture center. Please select a different cell", false, "error"); return;} + if (pack.cells.h[center] < 20) { + tip('You cannot place culture center into the water. Please click on a land cell', false, 'error'); + return; + } + const occupied = pack.cultures.some((c) => !c.removed && c.center === center); + if (occupied) { + tip('This cell is already a culture center. Please select a different cell', false, 'error'); + return; + } if (d3.event.shiftKey === false) exitAddCultureMode(); Cultures.add(center); @@ -681,31 +813,30 @@ function editCultures() { } function downloadCulturesData() { - const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value; - let data = "Id,Culture,Color,Cells,Expansionism,Type,Area "+unit+",Population,Namesbase,Emblems Shape\n"; // headers + const unit = areaUnit.value === 'square' ? distanceUnitInput.value + '2' : areaUnit.value; + let data = 'Id,Culture,Color,Cells,Expansionism,Type,Area ' + unit + ',Population,Namesbase,Emblems Shape\n'; // headers - body.querySelectorAll(":scope > div").forEach(function(el) { - data += el.dataset.id + ","; - data += el.dataset.name + ","; - data += el.dataset.color + ","; - data += el.dataset.cells + ","; - data += el.dataset.expansionism + ","; - data += el.dataset.type + ","; - data += el.dataset.area + ","; - data += el.dataset.population + ","; + body.querySelectorAll(':scope > div').forEach(function (el) { + data += el.dataset.id + ','; + data += el.dataset.name + ','; + data += el.dataset.color + ','; + data += el.dataset.cells + ','; + data += el.dataset.expansionism + ','; + data += el.dataset.type + ','; + data += el.dataset.area + ','; + data += el.dataset.population + ','; const base = +el.dataset.base; - data += nameBases[base].name + ","; - data += el.dataset.emblems + "\n"; + data += nameBases[base].name + ','; + data += el.dataset.emblems + '\n'; }); - const name = getFileName("Cultures") + ".csv"; + const name = getFileName('Cultures') + '.csv'; downloadFile(data, name); } function closeCulturesEditor() { - debug.select("#cultureCenters").remove(); - exitCulturesManualAssignment("close"); - exitAddCultureMode() + debug.select('#cultureCenters').remove(); + exitCulturesManualAssignment('close'); + exitAddCultureMode(); } - } diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js index f62d2ab0..7ee508d4 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -1,97 +1,116 @@ // heightmap-editor module. To be added to window as for now -"use strict"; +'use strict'; function editHeightmap() { - void function selectEditMode() { + void (function selectEditMode() { alertMessage.innerHTML = `Heightmap is a core element on which all other data (rivers, burgs, states etc) is based. So the best edit approach is to erase the secondary data and let the system automatically regenerate it on edit completion.

Erase mode also allows you Convert an Image into a heightmap or use Template Editor.

You can keep the data, but you won't be able to change the coastline.

Try risk mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.

Please save the map before editing the heightmap!

-

Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.

`; +

Check out ${link('https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization', 'wiki')} for guidance.

`; - $("#alert").dialog({resizable: false, title: "Edit Heightmap", width: "28em", + $('#alert').dialog({ + resizable: false, + title: 'Edit Heightmap', + width: '28em', buttons: { - Erase: function() {enterHeightmapEditMode("erase");}, - Keep: function() {enterHeightmapEditMode("keep");}, - Risk: function() {enterHeightmapEditMode("risk");}, - Cancel: function() {$(this).dialog("close");} + Erase: function () { + enterHeightmapEditMode('erase'); + }, + Keep: function () { + enterHeightmapEditMode('keep'); + }, + Risk: function () { + enterHeightmapEditMode('risk'); + }, + Cancel: function () { + $(this).dialog('close'); + } } }); - }() + })(); restartHistory(); - viewbox.insert("g", "#terrs").attr("id", "heights"); + viewbox.insert('g', '#terrs').attr('id', 'heights'); if (modules.editHeightmap) return; modules.editHeightmap = true; // add listeners - document.getElementById("paintBrushes").addEventListener("click", openBrushesPanel); - document.getElementById("applyTemplate").addEventListener("click", openTemplateEditor); - document.getElementById("convertImage").addEventListener("click", openImageConverter); - document.getElementById("heightmapPreview").addEventListener("click", toggleHeightmapPreview); - document.getElementById("heightmap3DView").addEventListener("click", changeViewMode); - document.getElementById("finalizeHeightmap").addEventListener("click", finalizeHeightmap); - document.getElementById("renderOcean").addEventListener("click", mockHeightmap); - document.getElementById("templateUndo").addEventListener("click", () => restoreHistory(edits.n-1)); - document.getElementById("templateRedo").addEventListener("click", () => restoreHistory(edits.n+1)); + document.getElementById('paintBrushes').addEventListener('click', openBrushesPanel); + document.getElementById('applyTemplate').addEventListener('click', openTemplateEditor); + document.getElementById('convertImage').addEventListener('click', openImageConverter); + document.getElementById('heightmapPreview').addEventListener('click', toggleHeightmapPreview); + document.getElementById('heightmap3DView').addEventListener('click', changeViewMode); + document.getElementById('finalizeHeightmap').addEventListener('click', finalizeHeightmap); + document.getElementById('renderOcean').addEventListener('click', mockHeightmap); + document.getElementById('templateUndo').addEventListener('click', () => restoreHistory(edits.n - 1)); + document.getElementById('templateRedo').addEventListener('click', () => restoreHistory(edits.n + 1)); function enterHeightmapEditMode(type) { - editHeightmap.layers = Array.from(mapLayers.querySelectorAll("li:not(.buttonoff)")).map(node => node.id); // store layers preset - editHeightmap.layers.forEach(l => document.getElementById(l).click()); // turn off all layers + editHeightmap.layers = Array.from(mapLayers.querySelectorAll('li:not(.buttonoff)')).map((node) => node.id); // store layers preset + editHeightmap.layers.forEach((l) => document.getElementById(l).click()); // turn off all layers customization = 1; closeDialogs(); tip('Heightmap edit mode is active. Click on "Exit Customization" to finalize the heightmap', true); - customizationMenu.style.display = "block"; - toolsContent.style.display = "none"; + customizationMenu.style.display = 'block'; + toolsContent.style.display = 'none'; heightmapEditMode.innerHTML = type; - if (type === "erase") { + if (type === 'erase') { undraw(); changeOnlyLand.checked = false; - } else if (type === "keep") { - viewbox.selectAll("#landmass, #lakes").style("display", "none"); + } else if (type === 'keep') { + viewbox.selectAll('#landmass, #lakes').style('display', 'none'); changeOnlyLand.checked = true; - } else if (type === "risk") { - defs.selectAll("#land, #water").selectAll("path").remove(); - viewbox.selectAll("#coastline path, #lakes path, #oceanLayers path").remove(); + } else if (type === 'risk') { + defs.selectAll('#land, #water').selectAll('path').remove(); + viewbox.selectAll('#coastline path, #lakes path, #oceanLayers path').remove(); changeOnlyLand.checked = false; } // show convert and template buttons for Erase mode only - applyTemplate.style.display = type === "erase" ? "inline-block" : "none"; - convertImage.style.display = type === "erase" ? "inline-block" : "none"; + applyTemplate.style.display = type === 'erase' ? 'inline-block' : 'none'; + convertImage.style.display = type === 'erase' ? 'inline-block' : 'none'; // hide erosion checkbox if mode is Keep - changeHeightsBox.style.display = type === "keep" ? "none" : "inline-block"; + changeHeightsBox.style.display = type === 'keep' ? 'none' : 'inline-block'; // show finalize button - if (!sessionStorage.getItem("noExitButtonAnimation")) { - sessionStorage.setItem("noExitButtonAnimation", true); + if (!sessionStorage.getItem('noExitButtonAnimation')) { + sessionStorage.setItem('noExitButtonAnimation', true); exitCustomization.style.opacity = 0; const width = 12 * uiSizeOutput.value * 11; - exitCustomization.style.right = (svgWidth - width) / 2 + "px"; - exitCustomization.style.bottom = svgHeight / 2 + "px"; - exitCustomization.style.transform = "scale(2)"; - exitCustomization.style.display = "block"; - d3.select("#exitCustomization") - .transition().duration(1000).style("opacity", 1) - .transition().duration(2000).ease(d3.easeSinInOut).style("right", "10px").style("bottom", "10px").style("transform", "scale(1)"); - } else exitCustomization.style.display = "block"; + exitCustomization.style.right = (svgWidth - width) / 2 + 'px'; + exitCustomization.style.bottom = svgHeight / 2 + 'px'; + exitCustomization.style.transform = 'scale(2)'; + exitCustomization.style.display = 'block'; + d3.select('#exitCustomization') + .transition() + .duration(1000) + .style('opacity', 1) + .transition() + .duration(2000) + .ease(d3.easeSinInOut) + .style('right', '10px') + .style('bottom', '10px') + .style('transform', 'scale(1)'); + } else exitCustomization.style.display = 'block'; openBrushesPanel(); - turnButtonOn("toggleHeight"); - layersPreset.value = "heightmap"; + turnButtonOn('toggleHeight'); + layersPreset.value = 'heightmap'; layersPreset.disabled = true; mockHeightmap(); - viewbox.on("touchmove mousemove", moveCursor); + viewbox.on('touchmove mousemove', moveCursor); } function moveCursor() { - const p = d3.mouse(this), cell = findGridCell(p[0], p[1]); + const p = d3.mouse(this), + cell = findGridCell(p[0], p[1]); heightmapInfoX.innerHTML = rn(p[0]); heightmapInfoY.innerHTML = rn(p[1]); heightmapInfoCell.innerHTML = cell; @@ -99,34 +118,35 @@ function editHeightmap() { if (tooltip.dataset.main) showMainTip(); // move radius circle if drag mode is active - const pressed = document.getElementById("brushesButtons").querySelector("button.pressed"); + const pressed = document.getElementById('brushesButtons').querySelector('button.pressed'); if (!pressed) return; - moveCircle(p[0], p[1], brushRadius.valueAsNumber, "#333"); + moveCircle(p[0], p[1], brushRadius.valueAsNumber, '#333'); } // get user-friendly (real-world) height value from map data function getHeight(h) { const unit = heightUnit.value; let unitRatio = 3.281; // default calculations are in feet - if (unit === "m") unitRatio = 1; // if meter - else if (unit === "f") unitRatio = 0.5468; // if fathom + if (unit === 'm') unitRatio = 1; + // if meter + else if (unit === 'f') unitRatio = 0.5468; // if fathom let height = -990; if (h >= 20) height = Math.pow(h - 18, +heightExponentInput.value); - else if (h < 20 && h > 0) height = (h - 20) / h * 50; + else if (h < 20 && h > 0) height = ((h - 20) / h) * 50; - return rn(height * unitRatio) + " " + unit; + return rn(height * unitRatio) + ' ' + unit; } // Exit customization mode function finalizeHeightmap() { - if (viewbox.select("#heights").selectAll("*").size() < 200) { - tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error"); + if (viewbox.select('#heights').selectAll('*').size() < 200) { + tip('Insufficient land area! There should be at least 200 land cells to finalize the heightmap', null, 'error'); return; } - if (document.getElementById("imageConverter").offsetParent) { - tip("Please exit the Image Conversion mode first", null, "error"); + if (document.getElementById('imageConverter').offsetParent) { + tip('Please exit the Image Conversion mode first', null, 'error'); return; } @@ -135,37 +155,41 @@ function editHeightmap() { undo.disabled = templateUndo.disabled = true; customization = 0; - customizationMenu.style.display = "none"; - if (document.getElementById("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block"; + customizationMenu.style.display = 'none'; + if (document.getElementById('options').querySelector('.tab > button.active').id === 'toolsTab') toolsContent.style.display = 'block'; layersPreset.disabled = false; - exitCustomization.style.display = "none"; // hide finalize button + exitCustomization.style.display = 'none'; // hide finalize button restoreDefaultEvents(); clearMainTip(); closeDialogs(); resetZoom(); - if (document.getElementById("preview")) document.getElementById("preview").remove(); - if (document.getElementById("canvas3d")) enterStandardView(); + if (document.getElementById('preview')) document.getElementById('preview').remove(); + if (document.getElementById('canvas3d')) enterStandardView(); const mode = heightmapEditMode.innerHTML; - if (mode === "erase") regenerateErasedData(); - else if (mode === "keep") restoreKeptData(); - else if (mode === "risk") restoreRiskedData(); + if (mode === 'erase') regenerateErasedData(); + else if (mode === 'keep') restoreKeptData(); + else if (mode === 'risk') restoreRiskedData(); // restore initial layers //viewbox.select("#heights").remove(); - document.getElementById("heights").remove(); - turnButtonOff("toggleHeight"); - document.getElementById("mapLayers").querySelectorAll("li").forEach(function(e) { - if (editHeightmap.layers.includes(e.id) && !layerIsOn(e.id)) e.click(); // turn on - else if (!editHeightmap.layers.includes(e.id) && layerIsOn(e.id)) e.click(); // turn off - }); + document.getElementById('heights').remove(); + turnButtonOff('toggleHeight'); + document + .getElementById('mapLayers') + .querySelectorAll('li') + .forEach(function (e) { + if (editHeightmap.layers.includes(e.id) && !layerIsOn(e.id)) e.click(); + // turn on + else if (!editHeightmap.layers.includes(e.id) && layerIsOn(e.id)) e.click(); // turn off + }); getCurrentPreset(); } function regenerateErasedData() { - INFO && console.group("Edit Heightmap"); - TIME && console.time("regenerateErasedData"); + INFO && console.group('Edit Heightmap'); + TIME && console.time('regenerateErasedData'); const change = changeHeights.checked; markFeatures(); @@ -207,20 +231,20 @@ function editHeightmap() { Military.generate(); addMarkers(); addZones(); - TIME && console.timeEnd("regenerateErasedData"); - INFO && console.groupEnd("Edit Heightmap"); + TIME && console.timeEnd('regenerateErasedData'); + INFO && console.groupEnd('Edit Heightmap'); } function restoreKeptData() { - viewbox.selectAll("#landmass, #lakes").style("display", null); + viewbox.selectAll('#landmass, #lakes').style('display', null); for (const i of pack.cells.i) { pack.cells.h[i] = grid.cells.h[pack.cells.g[i]]; } } function restoreRiskedData() { - INFO && console.group("Edit Heightmap"); - TIME && console.time("restoreRiskedData"); + INFO && console.group('Edit Heightmap'); + TIME && console.time('restoreRiskedData'); // assign pack data to grid cells const l = grid.cells.i.length; @@ -275,13 +299,13 @@ function editHeightmap() { } // recalculate zones to grid - zones.selectAll("g").each(function() { + zones.selectAll('g').each(function () { const zone = d3.select(this); - const dataCells = zone.attr("data-cells"); - const cells = dataCells ? dataCells.split(",").map(i => +i) : []; - const g = cells.map(i => pack.cells.g[i]); - zone.attr("data-cells", g); - zone.selectAll("*").remove(); + const dataCells = zone.attr('data-cells'); + const cells = dataCells ? dataCells.split(',').map((i) => +i) : []; + const g = cells.map((i) => pack.cells.g[i]); + zone.attr('data-cells', g); + zone.selectAll('*').remove(); }); markFeatures(); @@ -339,14 +363,12 @@ function editHeightmap() { } // find closest land cell to burg - const findBurgCell = function(x, y) { + const findBurgCell = function (x, y) { let i = findCell(x, y); if (pack.cells.h[i] >= 20) return i; - const dist = pack.cells.c[i].map(c => - pack.cells.h[c] < 20 ? Infinity : (pack.cells.p[c][0] - x) ** 2 + (pack.cells.p[c][1] - y) ** 2 - ); + const dist = pack.cells.c[i].map((c) => (pack.cells.h[c] < 20 ? Infinity : (pack.cells.p[c][0] - x) ** 2 + (pack.cells.p[c][1] - y) ** 2)); return pack.cells.c[i][d3.scan(dist)]; - } + }; // find best cell for burgs for (const b of pack.burgs) { @@ -361,18 +383,21 @@ function editHeightmap() { for (const p of pack.provinces) { if (!p.i || p.removed) continue; - const provCells = pack.cells.i.filter(i => pack.cells.province[i] === p.i); + const provCells = pack.cells.i.filter((i) => pack.cells.province[i] === p.i); if (!provCells.length) { const state = p.state; const stateProvs = pack.states[state].provinces; if (stateProvs.includes(p.i)) pack.states[state].provinces.splice(stateProvs.indexOf(p), 1); - + p.removed = true; continue; } if (p.burg && !pack.burgs[p.burg].removed) p.center = pack.burgs[p.burg].cell; - else {p.center = provCells[0]; p.burg = pack.cells.burg[p.center];} + else { + p.center = provCells[0]; + p.burg = pack.cells.burg[p.center]; + } } for (const c of pack.cultures) { @@ -390,28 +415,33 @@ function editHeightmap() { } // restore zones from grid - zones.selectAll("g").each(function() { + zones.selectAll('g').each(function () { const zone = d3.select(this); - const g = zone.attr("data-cells"); - const gCells = g ? g.split(",").map(i => +i) : []; - const cells = pack.cells.i.filter(i => gCells.includes(pack.cells.g[i])); + const g = zone.attr('data-cells'); + const gCells = g ? g.split(',').map((i) => +i) : []; + const cells = pack.cells.i.filter((i) => gCells.includes(pack.cells.g[i])); - zone.attr("data-cells", cells); - zone.selectAll("*").remove(); - const base = zone.attr("id") + "_"; // id generic part - zone.selectAll("*").data(cells).enter().append("polygon") - .attr("points", d => getPackPolygon(d)).attr("id", d => base + d); + zone.attr('data-cells', cells); + zone.selectAll('*').remove(); + const base = zone.attr('id') + '_'; // id generic part + zone + .selectAll('*') + .data(cells) + .enter() + .append('polygon') + .attr('points', (d) => getPackPolygon(d)) + .attr('id', (d) => base + d); }); - TIME && console.timeEnd("restoreRiskedData"); - INFO && console.groupEnd("Edit Heightmap"); + TIME && console.timeEnd('restoreRiskedData'); + INFO && console.groupEnd('Edit Heightmap'); } // trigger heightmap redraw and history update if at least 1 cell is changed function updateHeightmap() { const prev = last(edits); - const changed = grid.cells.h.reduce((s, h, i) => h !== prev[i] ? s+1 : s, 0); - tip("Cells changed: " + changed); + const changed = grid.cells.h.reduce((s, h, i) => (h !== prev[i] ? s + 1 : s), 0); + tip('Cells changed: ' + changed); if (!changed) return; // check ocean cells are not checged if olny land edit is allowed @@ -427,11 +457,16 @@ function editHeightmap() { // draw or update heightmap function mockHeightmap() { - 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(); - viewbox.select("#heights").selectAll("polygon").data(data).join("polygon") - .attr("points", d => getGridPolygon(d)).attr("id", d => "cell"+d) - .attr("fill", d => getColor(grid.cells.h[d], scheme)); + viewbox + .select('#heights') + .selectAll('polygon') + .data(data) + .join('polygon') + .attr('points', (d) => getGridPolygon(d)) + .attr('id', (d) => 'cell' + d) + .attr('fill', (d) => getColor(grid.cells.h[d], scheme)); } // draw or update heightmap for a selection of cells @@ -439,18 +474,26 @@ function editHeightmap() { const ocean = renderOcean.checked; const scheme = getColorScheme(); - selection.forEach(function(i) { - let cell = viewbox.select("#heights").select("#cell"+i); - if (!ocean && grid.cells.h[i] < 20) {cell.remove(); return;} - if (!cell.size()) cell = viewbox.select("#heights").append("polygon").attr("points", getGridPolygon(i)).attr("id", "cell"+i); - cell.attr("fill", getColor(grid.cells.h[i], scheme)); + selection.forEach(function (i) { + let cell = viewbox.select('#heights').select('#cell' + i); + if (!ocean && grid.cells.h[i] < 20) { + cell.remove(); + return; + } + if (!cell.size()) + cell = viewbox + .select('#heights') + .append('polygon') + .attr('points', getGridPolygon(i)) + .attr('id', 'cell' + i); + cell.attr('fill', getColor(grid.cells.h[i], scheme)); }); } function updateStatistics() { - const landCells = grid.cells.h.reduce((s, h) => h >= 20 ? s+1 : s); - landmassCounter.innerHTML = `${landCells} (${rn(landCells/grid.cells.i.length*100)}%)`; - landmassAverage.innerHTML = rn(d3.mean(grid.cells.h)); + const landCells = grid.cells.h.reduce((s, h) => (h >= 20 ? s + 1 : s)); + landmassCounter.innerHTML = `${landCells} (${rn((landCells / grid.cells.i.length) * 100)}%)`; + landmassAverage.innerHTML = rn(d3.mean(grid.cells.h)); } function updateHistory(noStat) { @@ -463,8 +506,8 @@ function editHeightmap() { redo.disabled = templateRedo.disabled = true; if (!noStat) { updateStatistics(); - if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened - if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened + if (document.getElementById('preview')) drawHeightmapPreview(); // update heightmap preview if opened + if (document.getElementById('canvas3d')) ThreeD.redraw(); // update 3d heightmap preview if opened } } @@ -477,9 +520,9 @@ function editHeightmap() { grid.cells.h = edits[edits.n - 1].slice(); mockHeightmap(); updateStatistics(); - - if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened - if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened + + if (document.getElementById('preview')) drawHeightmapPreview(); // update heightmap preview if opened + if (document.getElementById('canvas3d')) ThreeD.redraw(); // update 3d heightmap preview if opened } // restart edits from 1st step @@ -492,58 +535,64 @@ function editHeightmap() { } function openBrushesPanel() { - if ($("#brushesPanel").is(":visible")) return; - $("#brushesPanel").dialog({ - title: "Paint Brushes", resizable: false, - position: {my: "right top", at: "right-10 top+10", of: "svg"} - }).on('dialogclose', exitBrushMode); + if ($('#brushesPanel').is(':visible')) return; + $('#brushesPanel') + .dialog({ + title: 'Paint Brushes', + resizable: false, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg'} + }) + .on('dialogclose', exitBrushMode); if (modules.openBrushesPanel) return; modules.openBrushesPanel = true; // add listeners - document.getElementById("brushesButtons").addEventListener("click", e => toggleBrushMode(e)); - document.getElementById("changeOnlyLand").addEventListener("click", e => changeOnlyLandClick(e)); - document.getElementById("undo").addEventListener("click", () => restoreHistory(edits.n-1)); - document.getElementById("redo").addEventListener("click", () => restoreHistory(edits.n+1)); - document.getElementById("rescaleShow").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "none"; - document.getElementById("rescaleSection").style.display = "block"; + document.getElementById('brushesButtons').addEventListener('click', (e) => toggleBrushMode(e)); + document.getElementById('changeOnlyLand').addEventListener('click', (e) => changeOnlyLandClick(e)); + document.getElementById('undo').addEventListener('click', () => restoreHistory(edits.n - 1)); + document.getElementById('redo').addEventListener('click', () => restoreHistory(edits.n + 1)); + document.getElementById('rescaleShow').addEventListener('click', () => { + document.getElementById('modifyButtons').style.display = 'none'; + document.getElementById('rescaleSection').style.display = 'block'; }); - document.getElementById("rescaleHide").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "block"; - document.getElementById("rescaleSection").style.display = "none"; - }); - document.getElementById("rescaler").addEventListener("change", (e) => rescale(e.target.valueAsNumber)); - document.getElementById("rescaleCondShow").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "none"; - document.getElementById("rescaleCondSection").style.display = "block"; + document.getElementById('rescaleHide').addEventListener('click', () => { + document.getElementById('modifyButtons').style.display = 'block'; + document.getElementById('rescaleSection').style.display = 'none'; }); - document.getElementById("rescaleCondHide").addEventListener("click", () => { - document.getElementById("modifyButtons").style.display = "block"; - document.getElementById("rescaleCondSection").style.display = "none"; + document.getElementById('rescaler').addEventListener('change', (e) => rescale(e.target.valueAsNumber)); + document.getElementById('rescaleCondShow').addEventListener('click', () => { + document.getElementById('modifyButtons').style.display = 'none'; + document.getElementById('rescaleCondSection').style.display = 'block'; }); - document.getElementById("rescaleExecute").addEventListener("click", rescaleWithCondition); - document.getElementById("smoothHeights").addEventListener("click", smoothAllHeights); - document.getElementById("disruptHeights").addEventListener("click", disruptAllHeights); - document.getElementById("brushClear").addEventListener("click", startFromScratch); - - function exitBrushMode() { - const pressed = document.querySelector("#brushesButtons > button.pressed"); - if (!pressed) return; - pressed.classList.remove("pressed"); + document.getElementById('rescaleCondHide').addEventListener('click', () => { + document.getElementById('modifyButtons').style.display = 'block'; + document.getElementById('rescaleCondSection').style.display = 'none'; + }); + document.getElementById('rescaleExecute').addEventListener('click', rescaleWithCondition); + document.getElementById('smoothHeights').addEventListener('click', smoothAllHeights); + document.getElementById('disruptHeights').addEventListener('click', disruptAllHeights); + document.getElementById('brushClear').addEventListener('click', startFromScratch); - viewbox.style("cursor", "default").on(".drag", null); + function exitBrushMode() { + const pressed = document.querySelector('#brushesButtons > button.pressed'); + if (!pressed) return; + pressed.classList.remove('pressed'); + + viewbox.style('cursor', 'default').on('.drag', null); removeCircle(); - document.getElementById("brushesSliders").style.display = "none"; + document.getElementById('brushesSliders').style.display = 'none'; } function toggleBrushMode(e) { - if (e.target.classList.contains("pressed")) {exitBrushMode(); return;} + if (e.target.classList.contains('pressed')) { + exitBrushMode(); + return; + } exitBrushMode(); - document.getElementById("brushesSliders").style.display = "block"; - e.target.classList.add("pressed"); - viewbox.style("cursor", "crosshair").call(d3.drag().on("start", dragBrush)); + document.getElementById('brushesSliders').style.display = 'block'; + e.target.classList.add('pressed'); + viewbox.style('cursor', 'crosshair').call(d3.drag().on('start', dragBrush)); } function dragBrush() { @@ -551,65 +600,73 @@ function editHeightmap() { const point = d3.mouse(this); const start = findGridCell(point[0], point[1]); - d3.event.on("drag", () => { + d3.event.on('drag', () => { const p = d3.mouse(this); - moveCircle(p[0], p[1], r, "#333"); + moveCircle(p[0], p[1], r, '#333'); if (~~d3.event.sourceEvent.timeStamp % 5 != 0) return; // slow down the edit const inRadius = findGridAll(p[0], p[1], r); - const selection = changeOnlyLand.checked ? inRadius.filter(i => grid.cells.h[i] >= 20) : inRadius; + const selection = changeOnlyLand.checked ? inRadius.filter((i) => grid.cells.h[i] >= 20) : inRadius; if (selection && selection.length) changeHeightForSelection(selection, start); }); - d3.event.on("end", updateHeightmap); + d3.event.on('end', updateHeightmap); } function changeHeightForSelection(s, start) { const power = brushPower.valueAsNumber; const interpolate = d3.interpolateRound(power, 1); const land = changeOnlyLand.checked; - function lim(v) {return Math.max(Math.min(v, 100), land ? 20 : 0);} + function lim(v) { + return Math.max(Math.min(v, 100), land ? 20 : 0); + } const h = grid.cells.h; - const brush = document.querySelector("#brushesButtons > button.pressed").id; - if (brush === "brushRaise") s.forEach(i => h[i] = h[i] < 20 ? 20 : lim(h[i] + power)); else - if (brush === "brushElevate") s.forEach((i,d) => h[i] = lim(h[i] + interpolate(d/Math.max(s.length-1, 1)))); else - if (brush === "brushLower") s.forEach(i => h[i] = lim(h[i] - power)); else - if (brush === "brushDepress") s.forEach((i,d) => h[i] = lim(h[i] - interpolate(d/Math.max(s.length-1, 1)))); else - if (brush === "brushAlign") s.forEach(i => h[i] = lim(h[start])); else - if (brush === "brushSmooth") s.forEach(i => h[i] = rn((d3.mean(grid.cells.c[i].filter(i => land ? h[i] >= 20 : 1).map(c => h[c])) + h[i]*(10-power) + .6) / (11-power),1)); else - if (brush === "brushDisrupt") s.forEach(i => h[i] = h[i] < 15 ? h[i] : lim(h[i] + power/1.6 - Math.random()*power)); + const brush = document.querySelector('#brushesButtons > button.pressed').id; + if (brush === 'brushRaise') s.forEach((i) => (h[i] = h[i] < 20 ? 20 : lim(h[i] + power))); + else if (brush === 'brushElevate') s.forEach((i, d) => (h[i] = lim(h[i] + interpolate(d / Math.max(s.length - 1, 1))))); + else if (brush === 'brushLower') s.forEach((i) => (h[i] = lim(h[i] - power))); + else if (brush === 'brushDepress') s.forEach((i, d) => (h[i] = lim(h[i] - interpolate(d / Math.max(s.length - 1, 1))))); + else if (brush === 'brushAlign') s.forEach((i) => (h[i] = lim(h[start]))); + else if (brush === 'brushSmooth') s.forEach((i) => (h[i] = rn((d3.mean(grid.cells.c[i].filter((i) => (land ? h[i] >= 20 : 1)).map((c) => h[c])) + h[i] * (10 - power) + 0.6) / (11 - power), 1))); + else if (brush === 'brushDisrupt') s.forEach((i) => (h[i] = h[i] < 15 ? h[i] : lim(h[i] + power / 1.6 - Math.random() * power))); mockHeightmapSelection(s); // updateHistory(); uncomment to update history every step } function changeOnlyLandClick(e) { - if (heightmapEditMode.innerHTML !== "keep") return; + if (heightmapEditMode.innerHTML !== 'keep') return; e.preventDefault(); - tip("You cannot change the coastline in 'Keep' edit mode", false, "error"); + tip("You cannot change the coastline in 'Keep' edit mode", false, 'error'); } function rescale(v) { const land = changeOnlyLand.checked; - grid.cells.h = grid.cells.h.map(h => land && (h < 20 || h+v < 20) ? h : lim(h+v)); + grid.cells.h = grid.cells.h.map((h) => (land && (h < 20 || h + v < 20) ? h : lim(h + v))); updateHeightmap(); - document.getElementById("rescaler").value = 0; + document.getElementById('rescaler').value = 0; } function rescaleWithCondition() { - const range = rescaleLower.value + "-" + rescaleHigher.value; + const range = rescaleLower.value + '-' + rescaleHigher.value; const operator = conditionSign.value; const operand = rescaleModifier.valueAsNumber; - if (Number.isNaN(operand)) {tip("Operand should be a number", false, "error"); return;} - if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) {tip("Operand should be an integer", false, "error"); return;} + if (Number.isNaN(operand)) { + tip('Operand should be a number', false, 'error'); + return; + } + if ((operator === 'add' || operator === 'subtract') && !Number.isInteger(operand)) { + tip('Operand should be an integer', false, 'error'); + return; + } + + if (operator === 'multiply') HeightmapGenerator.modify(range, 0, operand, 0); + else if (operator === 'divide') HeightmapGenerator.modify(range, 0, 1 / operand, 0); + else if (operator === 'add') HeightmapGenerator.modify(range, operand, 1, 0); + else if (operator === 'subtract') HeightmapGenerator.modify(range, -1 * operand, 1, 0); + else if (operator === 'exponent') HeightmapGenerator.modify(range, 0, 1, operand); - if (operator === "multiply") HeightmapGenerator.modify(range, 0, operand, 0); else - if (operator === "divide") HeightmapGenerator.modify(range, 0, 1 / operand, 0); else - if (operator === "add") HeightmapGenerator.modify(range, operand, 1, 0); else - if (operator === "subtract") HeightmapGenerator.modify(range, -1 * operand, 1, 0); else - if (operator === "exponent") HeightmapGenerator.modify(range, 0, 1, operand); - updateHeightmap(); } @@ -619,79 +676,92 @@ function editHeightmap() { } function disruptAllHeights() { - grid.cells.h = grid.cells.h.map(h => h < 15 ? h : lim(h + 2.5 - Math.random() * 4)); + grid.cells.h = grid.cells.h.map((h) => (h < 15 ? h : lim(h + 2.5 - Math.random() * 4))); updateHeightmap(); } - + function startFromScratch() { - if (changeOnlyLand.checked) {tip("Not allowed when 'Change only land cells' mode is set", false, "error"); return;} - const someHeights = grid.cells.h.some(h => h); - if (!someHeights) {tip("Heightmap is already cleared, please do not click twice if not required", false, "error"); return;} + if (changeOnlyLand.checked) { + tip("Not allowed when 'Change only land cells' mode is set", false, 'error'); + return; + } + const someHeights = grid.cells.h.some((h) => h); + if (!someHeights) { + tip('Heightmap is already cleared, please do not click twice if not required', false, 'error'); + return; + } grid.cells.h = new Uint8Array(grid.cells.i.length); - viewbox.select("#heights").selectAll("*").remove(); + viewbox.select('#heights').selectAll('*').remove(); updateHistory(); } - } function openTemplateEditor() { - if ($("#templateEditor").is(":visible")) return; - const body = document.getElementById("templateBody"); + if ($('#templateEditor').is(':visible')) return; + const body = document.getElementById('templateBody'); - $("#templateEditor").dialog({ - title: "Template Editor", minHeight: "auto", width: "fit-content", resizable: false, - position: {my: "right top", at: "right-10 top+10", of: "svg"} + $('#templateEditor').dialog({ + title: 'Template Editor', + minHeight: 'auto', + width: 'fit-content', + resizable: false, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg'} }); if (modules.openTemplateEditor) return; modules.openTemplateEditor = true; - - $("#templateBody").sortable({items: "> div", handle: ".icon-resize-vertical", containment: "#templateBody", axis: "y"}); + + $('#templateBody').sortable({items: '> div', handle: '.icon-resize-vertical', containment: '#templateBody', axis: 'y'}); // add listeners - body.addEventListener("click", function(ev) { + body.addEventListener('click', function (ev) { const el = ev.target; - if (el.classList.contains("icon-check")) { - el.classList.remove("icon-check"); - el.classList.add("icon-check-empty"); - el.parentElement.style.opacity = .5; + if (el.classList.contains('icon-check')) { + el.classList.remove('icon-check'); + el.classList.add('icon-check-empty'); + el.parentElement.style.opacity = 0.5; body.dataset.changed = 1; return; } - if (el.classList.contains("icon-check-empty")) { - el.classList.add("icon-check"); - el.classList.remove("icon-check-empty"); + if (el.classList.contains('icon-check-empty')) { + el.classList.add('icon-check'); + el.classList.remove('icon-check-empty'); el.parentElement.style.opacity = 1; return; } - if (el.classList.contains("icon-trash-empty")) { - el.parentElement.remove(); return; + if (el.classList.contains('icon-trash-empty')) { + el.parentElement.remove(); + return; } }); - document.getElementById("templateTools").addEventListener("click", e => addStepOnClick(e)); - document.getElementById("templateSelect").addEventListener("change", e => selectTemplate(e)); - document.getElementById("templateRun").addEventListener("click", executeTemplate); - document.getElementById("templateSave").addEventListener("click", downloadTemplate); - document.getElementById("templateLoad").addEventListener("click", () => templateToLoad.click()); - document.getElementById("templateToLoad").addEventListener("change", function() {uploadFile(this, uploadTemplate)}); + document.getElementById('templateTools').addEventListener('click', (e) => addStepOnClick(e)); + document.getElementById('templateSelect').addEventListener('change', (e) => selectTemplate(e)); + document.getElementById('templateRun').addEventListener('click', executeTemplate); + document.getElementById('templateSave').addEventListener('click', downloadTemplate); + document.getElementById('templateLoad').addEventListener('click', () => templateToLoad.click()); + document.getElementById('templateToLoad').addEventListener('change', function () { + uploadFile(this, uploadTemplate); + }); function addStepOnClick(e) { - if (e.target.tagName !== "BUTTON") return; - const type = e.target.id.replace("template", ""); - document.getElementById("templateBody").dataset.changed = 1; + if (e.target.tagName !== 'BUTTON') return; + const type = e.target.id.replace('template', ''); + document.getElementById('templateBody').dataset.changed = 1; addStep(type); } function addStep(type, count, dist, arg4, arg5) { - const body = document.getElementById("templateBody"); - body.insertAdjacentHTML("beforeend", getStepHTML(type, count, dist, arg4, arg5)); - const elDist = body.querySelector("div:last-child").querySelector(".templateDist"); - if (elDist) elDist.addEventListener("change", setRange); - if (dist && elDist && elDist.tagName === "SELECT") { - for (const o of elDist.options) {if (o.value === dist) elDist.value = dist;} + const body = document.getElementById('templateBody'); + body.insertAdjacentHTML('beforeend', getStepHTML(type, count, dist, arg4, arg5)); + const elDist = body.querySelector('div:last-child').querySelector('.templateDist'); + if (elDist) elDist.addEventListener('change', setRange); + if (dist && elDist && elDist.tagName === 'SELECT') { + for (const o of elDist.options) { + if (o.value === dist) elDist.value = dist; + } if (elDist.value !== dist) { - const opt = document.createElement("option"); + const opt = document.createElement('option'); opt.value = opt.innerHTML = dist; elDist.add(opt); elDist.value = dist; @@ -705,24 +775,34 @@ function editHeightmap() { const Reorder = ``; const common = `
${Hide}
${type}
${Trash}${Reorder}`; - const TempY = `y:`; - const TempX = `x:`; - const Height = `h:`; - const Count = `n:`; + const TempY = `y:`; + const TempX = `x:`; + const Height = `h:`; + const Count = `n:`; const blob = `${common}${TempY}${TempX}${Height}${Count}
`; - if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return blob; - if (type === "Strait") return `${common}d:w:`; - if (type === "Add") return `${common}to:v:`; - if (type === "Multiply") return `${common}to:v:`; - if (type === "Smooth") return `${common}f:`; + if (type === 'Hill' || type === 'Pit' || type === 'Range' || type === 'Trough') return blob; + if (type === 'Strait') + return `${common}d:w:`; + if (type === 'Add') + return `${common}to:v:`; + if (type === 'Multiply') + return `${common}to:v:`; + if (type === 'Smooth') + return `${common}f:`; } function setRange(event) { - if (event.target.value !== "interval") return; + if (event.target.value !== 'interval') return; - prompt("Set a height interval. Avoid space, use hyphen as a separator", {default:"17-20"}, v => { - const opt = document.createElement("option"); + prompt('Set a height interval. Avoid space, use hyphen as a separator', {default: '17-20'}, (v) => { + const opt = document.createElement('option'); opt.value = opt.innerHTML = v; event.target.add(opt); event.target.value = v; @@ -730,445 +810,460 @@ function editHeightmap() { } function selectTemplate(e) { - const body = document.getElementById("templateBody"); - const steps = body.querySelectorAll("div").length; - const changed = +body.getAttribute("data-changed"); + const body = document.getElementById('templateBody'); + const steps = body.querySelectorAll('div').length; + const changed = +body.getAttribute('data-changed'); const template = e.target.value; - if (!steps || !changed) {changeTemplate(template); return;} + if (!steps || !changed) return changeTemplate(template); - alertMessage.innerHTML = "Are you sure you want to select a different template? All changes will be lost."; - $("#alert").dialog({resizable: false, title: "Change Template", - buttons: { - Change: function() {changeTemplate(template); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");}} - }); + const message = 'Are you sure you want to select a different template?
All changes will be lost'; + confirmationDialog({title: 'Change template', message, confirm: 'Change', onConfirm: () => changeTemplate(template)}); } function changeTemplate(template) { - const body = document.getElementById("templateBody"); - body.setAttribute("data-changed", 0); - body.innerHTML = ""; + const body = document.getElementById('templateBody'); + body.setAttribute('data-changed', 0); + body.innerHTML = ''; - if (template === "templateVolcano") { - addStep("Hill", "1", "90-100", "44-56", "40-60"); - addStep("Multiply", .8, "50-100"); - addStep("Range", "1.5", "30-55", "45-55", "40-60"); - addStep("Smooth", 2); - addStep("Hill", "1.5", "25-35", "25-30", "20-75"); - addStep("Hill", "1", "25-35", "75-80", "25-75"); - addStep("Hill", "0.5", "20-25", "10-15", "20-25"); + if (template === 'templateVolcano') { + addStep('Hill', '1', '90-100', '44-56', '40-60'); + addStep('Multiply', 0.8, '50-100'); + addStep('Range', '1.5', '30-55', '45-55', '40-60'); + addStep('Smooth', 2); + addStep('Hill', '1.5', '25-35', '25-30', '20-75'); + addStep('Hill', '1', '25-35', '75-80', '25-75'); + addStep('Hill', '0.5', '20-25', '10-15', '20-25'); + } else if (template === 'templateHighIsland') { + addStep('Hill', '1', '90-100', '65-75', '47-53'); + addStep('Add', 5, 'all'); + addStep('Hill', '6', '20-23', '25-55', '45-55'); + addStep('Range', '1', '40-50', '45-55', '45-55'); + addStep('Smooth', 2); + addStep('Trough', '2-3', '20-30', '20-30', '20-30'); + addStep('Trough', '2-3', '20-30', '60-80', '70-80'); + addStep('Hill', '1', '10-15', '60-60', '50-50'); + addStep('Hill', '1.5', '13-16', '15-20', '20-75'); + addStep('Multiply', 0.8, '20-100'); + addStep('Range', '1.5', '30-40', '15-85', '30-40'); + addStep('Range', '1.5', '30-40', '15-85', '60-70'); + addStep('Pit', '2-3', '10-15', '15-85', '20-80'); + } else if (template === 'templateLowIsland') { + addStep('Hill', '1', '90-99', '60-80', '45-55'); + addStep('Hill', '4-5', '25-35', '20-65', '40-60'); + addStep('Range', '1', '40-50', '45-55', '45-55'); + addStep('Smooth', 3); + addStep('Trough', '1.5', '20-30', '15-85', '20-30'); + addStep('Trough', '1.5', '20-30', '15-85', '70-80'); + addStep('Hill', '1.5', '10-15', '5-15', '20-80'); + addStep('Hill', '1', '10-15', '85-95', '70-80'); + addStep('Pit', '3-5', '10-15', '15-85', '20-80'); + addStep('Multiply', 0.4, '20-100'); + } else if (template === 'templateContinents') { + addStep('Hill', '1', '80-85', '75-80', '40-60'); + addStep('Hill', '1', '80-85', '20-25', '40-60'); + addStep('Multiply', 0.22, '20-100'); + addStep('Hill', '5-6', '15-20', '25-75', '20-82'); + addStep('Range', '.8', '30-60', '5-15', '20-45'); + addStep('Range', '.8', '30-60', '5-15', '55-80'); + addStep('Range', '0-3', '30-60', '80-90', '20-80'); + addStep('Trough', '3-4', '15-20', '15-85', '20-80'); + addStep('Strait', '2', 'vertical'); + addStep('Smooth', 2); + addStep('Trough', '1-2', '5-10', '45-55', '45-55'); + addStep('Pit', '3-4', '10-15', '15-85', '20-80'); + addStep('Hill', '1', '5-10', '40-60', '40-60'); + } else if (template === 'templateArchipelago') { + addStep('Add', 11, 'all'); + addStep('Range', '2-3', '40-60', '20-80', '20-80'); + addStep('Hill', '5', '15-20', '10-90', '30-70'); + addStep('Hill', '2', '10-15', '10-30', '20-80'); + addStep('Hill', '2', '10-15', '60-90', '20-80'); + addStep('Smooth', 3); + addStep('Trough', '10', '20-30', '5-95', '5-95'); + addStep('Strait', '2', 'vertical'); + addStep('Strait', '2', 'horizontal'); + } else if (template === 'templateAtoll') { + addStep('Hill', '1', '75-80', '50-60', '45-55'); + addStep('Hill', '1.5', '30-50', '25-75', '30-70'); + addStep('Hill', '.5', '30-50', '25-35', '30-70'); + addStep('Smooth', 1); + addStep('Multiply', 0.2, '25-100'); + addStep('Hill', '.5', '10-20', '50-55', '48-52'); + } else if (template === 'templateMediterranean') { + addStep('Range', '3-4', '30-50', '0-100', '0-10'); + addStep('Range', '3-4', '30-50', '0-100', '90-100'); + addStep('Hill', '5-6', '30-70', '0-100', '0-5'); + addStep('Hill', '5-6', '30-70', '0-100', '95-100'); + addStep('Smooth', 1); + addStep('Hill', '2-3', '30-70', '0-5', '20-80'); + addStep('Hill', '2-3', '30-70', '95-100', '20-80'); + addStep('Multiply', 0.8, 'land'); + addStep('Trough', '3-5', '40-50', '0-100', '0-10'); + addStep('Trough', '3-5', '40-50', '0-100', '90-100'); + } else if (template === 'templatePeninsula') { + addStep('Range', '2-3', '20-35', '40-50', '0-15'); + addStep('Add', 5, 'all'); + addStep('Hill', '1', '90-100', '10-90', '0-5'); + addStep('Add', 13, 'all'); + addStep('Hill', '3-4', '3-5', '5-95', '80-100'); + addStep('Hill', '1-2', '3-5', '5-95', '40-60'); + addStep('Trough', '5-6', '10-25', '5-95', '5-95'); + addStep('Smooth', 3); + } else if (template === 'templatePangea') { + addStep('Hill', '1-2', '25-40', '15-50', '0-10'); + addStep('Hill', '1-2', '5-40', '50-85', '0-10'); + addStep('Hill', '1-2', '25-40', '50-85', '90-100'); + addStep('Hill', '1-2', '5-40', '15-50', '90-100'); + addStep('Hill', '8-12', '20-40', '20-80', '48-52'); + addStep('Smooth', 2); + addStep('Multiply', 0.7, 'land'); + addStep('Trough', '3-4', '25-35', '5-95', '10-20'); + addStep('Trough', '3-4', '25-35', '5-95', '80-90'); + addStep('Range', '5-6', '30-40', '10-90', '35-65'); + } else if (template === 'templateIsthmus') { + addStep('Hill', '5-10', '15-30', '0-30', '0-20'); + addStep('Hill', '5-10', '15-30', '10-50', '20-40'); + addStep('Hill', '5-10', '15-30', '30-70', '40-60'); + addStep('Hill', '5-10', '15-30', '50-90', '60-80'); + addStep('Hill', '5-10', '15-30', '70-100', '80-100'); + addStep('Smooth', 2); + addStep('Trough', '4-8', '15-30', '0-30', '0-20'); + addStep('Trough', '4-8', '15-30', '10-50', '20-40'); + addStep('Trough', '4-8', '15-30', '30-70', '40-60'); + addStep('Trough', '4-8', '15-30', '50-90', '60-80'); + addStep('Trough', '4-8', '15-30', '70-100', '80-100'); + } else if (template === 'templateShattered') { + addStep('Hill', '8', '35-40', '15-85', '30-70'); + addStep('Trough', '10-20', '40-50', '5-95', '5-95'); + addStep('Range', '5-7', '30-40', '10-90', '20-80'); + addStep('Pit', '12-20', '30-40', '15-85', '20-80'); } - - else if (template === "templateHighIsland") { - addStep("Hill", "1", "90-100", "65-75", "47-53"); - addStep("Add", 5, "all"); - addStep("Hill", "6", "20-23", "25-55", "45-55"); - addStep("Range", "1", "40-50", "45-55", "45-55"); - addStep("Smooth", 2); - addStep("Trough", "2-3", "20-30", "20-30", "20-30"); - addStep("Trough", "2-3", "20-30", "60-80", "70-80"); - addStep("Hill", "1", "10-15", "60-60", "50-50"); - addStep("Hill", "1.5", "13-16", "15-20", "20-75"); - addStep("Multiply", .8, "20-100"); - addStep("Range", "1.5", "30-40", "15-85", "30-40"); - addStep("Range", "1.5", "30-40", "15-85", "60-70"); - addStep("Pit", "2-3", "10-15", "15-85", "20-80"); - } - - else if (template === "templateLowIsland") { - addStep("Hill", "1", "90-99", "60-80", "45-55"); - addStep("Hill", "4-5", "25-35", "20-65", "40-60"); - addStep("Range", "1", "40-50", "45-55", "45-55"); - addStep("Smooth", 3); - addStep("Trough", "1.5", "20-30", "15-85", "20-30"); - addStep("Trough", "1.5", "20-30", "15-85", "70-80"); - addStep("Hill", "1.5", "10-15", "5-15", "20-80"); - addStep("Hill", "1", "10-15", "85-95", "70-80"); - addStep("Pit", "3-5", "10-15", "15-85", "20-80"); - addStep("Multiply", .4, "20-100"); - } - - else if (template === "templateContinents") { - addStep("Hill", "1", "80-85", "75-80", "40-60"); - addStep("Hill", "1", "80-85", "20-25", "40-60"); - addStep("Multiply", .22, "20-100"); - addStep("Hill", "5-6", "15-20", "25-75", "20-82"); - addStep("Range", ".8", "30-60", "5-15", "20-45"); - addStep("Range", ".8", "30-60", "5-15", "55-80"); - addStep("Range", "0-3", "30-60", "80-90", "20-80"); - addStep("Trough", "3-4", "15-20", "15-85", "20-80"); - addStep("Strait", "2", "vertical"); - addStep("Smooth", 2); - addStep("Trough", "1-2", "5-10", "45-55", "45-55"); - addStep("Pit", "3-4", "10-15", "15-85", "20-80"); - addStep("Hill", "1", "5-10", "40-60", "40-60"); - } - - else if (template === "templateArchipelago") { - addStep("Add", 11, "all"); - addStep("Range", "2-3", "40-60", "20-80", "20-80"); - addStep("Hill", "5", "15-20", "10-90", "30-70"); - addStep("Hill", "2", "10-15", "10-30", "20-80"); - addStep("Hill", "2", "10-15", "60-90", "20-80"); - addStep("Smooth", 3); - addStep("Trough", "10", "20-30", "5-95", "5-95"); - addStep("Strait", "2", "vertical"); - addStep("Strait", "2", "horizontal"); - } - - else if (template === "templateAtoll") { - addStep("Hill", "1", "75-80", "50-60", "45-55"); - addStep("Hill", "1.5", "30-50", "25-75", "30-70"); - addStep("Hill", ".5", "30-50", "25-35", "30-70"); - addStep("Smooth", 1); - addStep("Multiply", .2, "25-100"); - addStep("Hill", ".5", "10-20", "50-55", "48-52"); - } - - else if (template === "templateMediterranean") { - addStep("Range", "3-4", "30-50", "0-100", "0-10"); - addStep("Range", "3-4", "30-50", "0-100", "90-100"); - addStep("Hill", "5-6", "30-70", "0-100", "0-5"); - addStep("Hill", "5-6", "30-70", "0-100", "95-100"); - addStep("Smooth", 1); - addStep("Hill", "2-3", "30-70", "0-5", "20-80"); - addStep("Hill", "2-3", "30-70", "95-100", "20-80"); - addStep("Multiply", .8, "land"); - addStep("Trough", "3-5", "40-50", "0-100", "0-10"); - addStep("Trough", "3-5", "40-50", "0-100", "90-100"); - } - - else if (template === "templatePeninsula") { - addStep("Range", "2-3", "20-35", "40-50", "0-15"); - addStep("Add", 5, "all"); - addStep("Hill", "1", "90-100", "10-90", "0-5"); - addStep("Add", 13, "all"); - addStep("Hill", "3-4", "3-5", "5-95", "80-100"); - addStep("Hill", "1-2", "3-5", "5-95", "40-60"); - addStep("Trough", "5-6", "10-25", "5-95", "5-95"); - addStep("Smooth", 3); - } - - else if (template === "templatePangea") { - addStep("Hill", "1-2", "25-40", "15-50", "0-10"); - addStep("Hill", "1-2", "5-40", "50-85", "0-10"); - addStep("Hill", "1-2", "25-40", "50-85", "90-100"); - addStep("Hill", "1-2", "5-40", "15-50", "90-100"); - addStep("Hill", "8-12", "20-40", "20-80", "48-52"); - addStep("Smooth", 2); - addStep("Multiply", .7, "land"); - addStep("Trough", "3-4", "25-35", "5-95", "10-20"); - addStep("Trough", "3-4", "25-35", "5-95", "80-90"); - addStep("Range", "5-6", "30-40", "10-90", "35-65"); - } - - else if (template === "templateIsthmus") { - addStep("Hill", "5-10", "15-30", "0-30", "0-20"); - addStep("Hill", "5-10", "15-30", "10-50", "20-40"); - addStep("Hill", "5-10", "15-30", "30-70", "40-60"); - addStep("Hill", "5-10", "15-30", "50-90", "60-80"); - addStep("Hill", "5-10", "15-30", "70-100", "80-100"); - addStep("Smooth", 2); - addStep("Trough", "4-8", "15-30", "0-30", "0-20"); - addStep("Trough", "4-8", "15-30", "10-50", "20-40"); - addStep("Trough", "4-8", "15-30", "30-70", "40-60"); - addStep("Trough", "4-8", "15-30", "50-90", "60-80"); - addStep("Trough", "4-8", "15-30", "70-100", "80-100"); - } - - else if (template === "templateShattered") { - addStep("Hill", "8", "35-40", "15-85", "30-70"); - addStep("Trough", "10-20", "40-50", "5-95", "5-95"); - addStep("Range", "5-7", "30-40", "10-90", "20-80"); - addStep("Pit", "12-20", "30-40", "15-85", "20-80"); - } - } function executeTemplate() { - const body = document.getElementById("templateBody"); - const steps = body.querySelectorAll("#templateBody > div"); + const body = document.getElementById('templateBody'); + const steps = body.querySelectorAll('#templateBody > div'); if (!steps.length) return; grid.cells.h = new Uint8Array(grid.cells.i.length); // clean all heights for (const s of steps) { - if (s.style.opacity == .5) continue; - const type = s.getAttribute("data-type"); - const elCount = s.querySelector(".templateCount") || ""; - const elHeight = s.querySelector(".templateHeight") || ""; + if (s.style.opacity == 0.5) continue; + const type = s.getAttribute('data-type'); + const elCount = s.querySelector('.templateCount') || ''; + const elHeight = s.querySelector('.templateHeight') || ''; - const elDist = s.querySelector(".templateDist"); + const elDist = s.querySelector('.templateDist'); const dist = elDist ? elDist.value : null; - const templateX = s.querySelector(".templateX"); + const templateX = s.querySelector('.templateX'); const x = templateX ? templateX.value : null; - const templateY = s.querySelector(".templateY"); + const templateY = s.querySelector('.templateY'); const y = templateY ? templateY.value : null; - if (type === "Hill") HeightmapGenerator.addHill(elCount.value, elHeight.value, x, y); else - if (type === "Pit") HeightmapGenerator.addPit(elCount.value, elHeight.value, x, y); else - if (type === "Range") HeightmapGenerator.addRange(elCount.value, elHeight.value, x, y); else - if (type === "Trough") HeightmapGenerator.addTrough(elCount.value, elHeight.value, x, y); else - if (type === "Strait") HeightmapGenerator.addStrait(elCount.value, dist); else - if (type === "Add") HeightmapGenerator.modify(dist, +elCount.value, 1); else - if (type === "Multiply") HeightmapGenerator.modify(dist, 0, +elCount.value); else - if (type === "Smooth") HeightmapGenerator.smooth(+elCount.value); + if (type === 'Hill') HeightmapGenerator.addHill(elCount.value, elHeight.value, x, y); + else if (type === 'Pit') HeightmapGenerator.addPit(elCount.value, elHeight.value, x, y); + else if (type === 'Range') HeightmapGenerator.addRange(elCount.value, elHeight.value, x, y); + else if (type === 'Trough') HeightmapGenerator.addTrough(elCount.value, elHeight.value, x, y); + else if (type === 'Strait') HeightmapGenerator.addStrait(elCount.value, dist); + else if (type === 'Add') HeightmapGenerator.modify(dist, +elCount.value, 1); + else if (type === 'Multiply') HeightmapGenerator.modify(dist, 0, +elCount.value); + else if (type === 'Smooth') HeightmapGenerator.smooth(+elCount.value); - updateHistory("noStat"); // update history every step + updateHistory('noStat'); // update history every step } updateStatistics(); mockHeightmap(); - if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened - if (document.getElementById("canvas3d")) ThreeD.redraw(); // update 3d heightmap preview if opened + if (document.getElementById('preview')) drawHeightmapPreview(); // update heightmap preview if opened + if (document.getElementById('canvas3d')) ThreeD.redraw(); // update 3d heightmap preview if opened } function downloadTemplate() { - const body = document.getElementById("templateBody"); + const body = document.getElementById('templateBody'); body.dataset.changed = 0; - const steps = body.querySelectorAll("#templateBody > div"); + const steps = body.querySelectorAll('#templateBody > div'); if (!steps.length) return; - let data = ""; + let data = ''; for (const s of steps) { - if (s.style.opacity == .5) continue; - const type = s.getAttribute("data-type"); - const elCount = s.querySelector(".templateCount"); - const count = elCount ? elCount.value : "0"; - const elHeight = s.querySelector(".templateHeight"); - const elDist = s.querySelector(".templateDist"); - const arg3 = elHeight ? elHeight.value : elDist ? elDist.value : "0"; - const templateX = s.querySelector(".templateX"); - const x = templateX ? templateX.value : "0"; - const templateY = s.querySelector(".templateY"); - const y = templateY ? templateY.value : "0"; + if (s.style.opacity == 0.5) continue; + const type = s.getAttribute('data-type'); + const elCount = s.querySelector('.templateCount'); + const count = elCount ? elCount.value : '0'; + const elHeight = s.querySelector('.templateHeight'); + const elDist = s.querySelector('.templateDist'); + const arg3 = elHeight ? elHeight.value : elDist ? elDist.value : '0'; + const templateX = s.querySelector('.templateX'); + const x = templateX ? templateX.value : '0'; + const templateY = s.querySelector('.templateY'); + const y = templateY ? templateY.value : '0'; data += `${type} ${count} ${arg3} ${x} ${y}\r\n`; } - const name = "template_" + Date.now() + ".txt"; + const name = 'template_' + Date.now() + '.txt'; downloadFile(data, name); } function uploadTemplate(dataLoaded) { - const steps = dataLoaded.split("\r\n"); - if (!steps.length) {tip("Cannot parse the template, please check the file", false, "error"); return;} - templateBody.innerHTML = ""; + const steps = dataLoaded.split('\r\n'); + if (!steps.length) { + tip('Cannot parse the template, please check the file', false, 'error'); + return; + } + templateBody.innerHTML = ''; for (const s of steps) { - const step = s.split(" "); - if (step.length !== 5) {ERROR && console.error("Cannot parse step, wrong arguments count", s); continue;} + const step = s.split(' '); + if (step.length !== 5) { + ERROR && console.error('Cannot parse step, wrong arguments count', s); + continue; + } addStep(step[0], step[1], step[2], step[3], step[4]); } - } } function openImageConverter() { - if ($("#imageConverter").is(":visible")) return; + if ($('#imageConverter').is(':visible')) return; imageToLoad.click(); - closeDialogs("#imageConverter"); + closeDialogs('#imageConverter'); - $("#imageConverter").dialog({ - title: "Image Converter", maxHeight: svgHeight*.8, minHeight: "auto", width: "20em", - position: {my: "right top", at: "right-10 top+10", of: "svg"}, + $('#imageConverter').dialog({ + title: 'Image Converter', + maxHeight: svgHeight * 0.8, + minHeight: 'auto', + width: '20em', + position: {my: 'right top', at: 'right-10 top+10', of: 'svg'}, beforeClose: closeImageConverter }); // create canvas for image - const canvas = document.createElement("canvas"); - canvas.id = "canvas"; + const canvas = document.createElement('canvas'); + canvas.id = 'canvas'; canvas.width = graphWidth; canvas.height = graphHeight; document.body.insertBefore(canvas, optionsContainer); setOverlayOpacity(0); clearMainTip(); - tip('Image Converter is opened. Upload image and assign height value for each color', false, "warn"); // main tip + tip('Image Converter is opened. Upload image and assign height value for each color', false, 'warn'); // main tip // remove all heights grid.cells.h = new Uint8Array(grid.cells.i.length); - viewbox.select("#heights").selectAll("*").remove(); + viewbox.select('#heights').selectAll('*').remove(); updateHistory(); if (modules.openImageConverter) return; modules.openImageConverter = true; // add color pallete - void function createColorPallete() { - d3.select("#imageConverterPalette").selectAll("div").data(d3.range(101)) - .enter().append("div").attr("data-color", i => i) - .style("background-color", i => color(1-(i < 20 ? i-5 : i) / 100)) - .style("width", i => i < 40 || i > 68 ? ".2em" : ".1em") - .on("touchmove mousemove", showPalleteHeight).on("click", assignHeight); - }() + void (function createColorPallete() { + d3.select('#imageConverterPalette') + .selectAll('div') + .data(d3.range(101)) + .enter() + .append('div') + .attr('data-color', (i) => i) + .style('background-color', (i) => color(1 - (i < 20 ? i - 5 : i) / 100)) + .style('width', (i) => (i < 40 || i > 68 ? '.2em' : '.1em')) + .on('touchmove mousemove', showPalleteHeight) + .on('click', assignHeight); + })(); // add listeners - document.getElementById("convertImageLoad").addEventListener("click", () => imageToLoad.click()); - document.getElementById("imageToLoad").addEventListener("change", loadImage); - document.getElementById("convertAutoLum").addEventListener("click", () => autoAssing("lum")); - document.getElementById("convertAutoHue").addEventListener("click", () => autoAssing("hue")); - document.getElementById("convertAutoFMG").addEventListener("click", () => autoAssing("scheme")); - document.getElementById("convertColorsButton").addEventListener("click", setConvertColorsNumber); - document.getElementById("convertComplete").addEventListener("click", applyConversion); - document.getElementById("convertCancel").addEventListener("click", cancelConversion); - document.getElementById("convertOverlay").addEventListener("input", function() {setOverlayOpacity(this.value)}); - document.getElementById("convertOverlayNumber").addEventListener("input", function() {setOverlayOpacity(this.value)}); + document.getElementById('convertImageLoad').addEventListener('click', () => imageToLoad.click()); + document.getElementById('imageToLoad').addEventListener('change', loadImage); + document.getElementById('convertAutoLum').addEventListener('click', () => autoAssing('lum')); + document.getElementById('convertAutoHue').addEventListener('click', () => autoAssing('hue')); + document.getElementById('convertAutoFMG').addEventListener('click', () => autoAssing('scheme')); + document.getElementById('convertColorsButton').addEventListener('click', setConvertColorsNumber); + document.getElementById('convertComplete').addEventListener('click', applyConversion); + document.getElementById('convertCancel').addEventListener('click', cancelConversion); + document.getElementById('convertOverlay').addEventListener('input', function () { + setOverlayOpacity(this.value); + }); + document.getElementById('convertOverlayNumber').addEventListener('input', function () { + setOverlayOpacity(this.value); + }); function showPalleteHeight() { - const height = +this.getAttribute("data-color"); + const height = +this.getAttribute('data-color'); colorsSelectValue.innerHTML = height; colorsSelectFriendly.innerHTML = getHeight(height); - const former = imageConverterPalette.querySelector(".hoveredColor") - if (former) former.className = ""; - this.className = "hoveredColor"; + const former = imageConverterPalette.querySelector('.hoveredColor'); + if (former) former.className = ''; + this.className = 'hoveredColor'; } function loadImage() { const file = this.files[0]; - this.value = ""; // reset input value to get triggered if the file is re-uploaded + this.value = ''; // reset input value to get triggered if the file is re-uploaded const reader = new FileReader(); - const img = new Image; - img.onload = function() { - const ctx = document.getElementById("canvas").getContext("2d"); + const img = new Image(); + img.onload = function () { + const ctx = document.getElementById('canvas').getContext('2d'); ctx.drawImage(img, 0, 0, graphWidth, graphHeight); heightsFromImage(+convertColors.value); resetZoom(); }; - reader.onloadend = () => img.src = reader.result; + reader.onloadend = () => (img.src = reader.result); reader.readAsDataURL(file); } function heightsFromImage(count) { - const sourceImage = document.getElementById("canvas"); - const sampleCanvas = document.createElement("canvas"); + const sourceImage = document.getElementById('canvas'); + const sampleCanvas = document.createElement('canvas'); sampleCanvas.width = grid.cellsX; sampleCanvas.height = grid.cellsY; sampleCanvas.getContext('2d').drawImage(sourceImage, 0, 0, grid.cellsX, grid.cellsY); - const q = new RgbQuant({colors:count}); + const q = new RgbQuant({colors: count}); q.sample(sampleCanvas); const data = q.reduce(sampleCanvas); const pallete = q.palette(true); - viewbox.select("#heights").selectAll("*").remove(); - d3.select("#imageConverter").selectAll("div.color-div").remove(); - colorsSelect.style.display = "block"; - colorsUnassigned.style.display = "block"; - colorsAssigned.style.display = "none"; + viewbox.select('#heights').selectAll('*').remove(); + d3.select('#imageConverter').selectAll('div.color-div').remove(); + colorsSelect.style.display = 'block'; + colorsUnassigned.style.display = 'block'; + colorsAssigned.style.display = 'none'; sampleCanvas.remove(); // no need to keep - viewbox.select("#heights").selectAll("polygon").data(grid.cells.i).join("polygon") - .attr("points", d => getGridPolygon(d)).attr("id", d => "cell"+d) - .attr("fill", d => `rgb(${data[d*4]}, ${data[d*4+1]}, ${data[d*4+2]})`) - .on("click", mapClicked); + viewbox + .select('#heights') + .selectAll('polygon') + .data(grid.cells.i) + .join('polygon') + .attr('points', (d) => getGridPolygon(d)) + .attr('id', (d) => 'cell' + d) + .attr('fill', (d) => `rgb(${data[d * 4]}, ${data[d * 4 + 1]}, ${data[d * 4 + 2]})`) + .on('click', mapClicked); - const colors = pallete.map(p => `rgb(${p[0]}, ${p[1]}, ${p[2]})`); - d3.select("#colorsUnassigned").selectAll("div").data(colors).enter().append("div") - .attr("data-color", i => i).style("background-color", i => i) - .attr("class", "color-div").on("click", colorClicked); + const colors = pallete.map((p) => `rgb(${p[0]}, ${p[1]}, ${p[2]})`); + d3.select('#colorsUnassigned') + .selectAll('div') + .data(colors) + .enter() + .append('div') + .attr('data-color', (i) => i) + .style('background-color', (i) => i) + .attr('class', 'color-div') + .on('click', colorClicked); - document.getElementById("colorsUnassignedNumber").innerHTML = colors.length; + document.getElementById('colorsUnassignedNumber').innerHTML = colors.length; } function mapClicked() { - const fill = this.getAttribute("fill"); + const fill = this.getAttribute('fill'); const palleteColor = imageConverter.querySelector(`div[data-color="${fill}"]`); palleteColor.click(); } function colorClicked() { - viewbox.select("#heights").selectAll(".selectedCell").attr("class", null); - const unselect = this.classList.contains("selectedColor"); + viewbox.select('#heights').selectAll('.selectedCell').attr('class', null); + const unselect = this.classList.contains('selectedColor'); - const selectedColor = imageConverter.querySelector("div.selectedColor"); - if (selectedColor) selectedColor.classList.remove("selectedColor"); - const hoveredColor = imageConverterPalette.querySelector("div.hoveredColor"); - if (hoveredColor) hoveredColor.classList.remove("hoveredColor"); + const selectedColor = imageConverter.querySelector('div.selectedColor'); + if (selectedColor) selectedColor.classList.remove('selectedColor'); + const hoveredColor = imageConverterPalette.querySelector('div.hoveredColor'); + if (hoveredColor) hoveredColor.classList.remove('hoveredColor'); colorsSelectValue.innerHTML = colorsSelectFriendly.innerHTML = 0; if (unselect) return; - this.classList.add("selectedColor"); + this.classList.add('selectedColor'); if (this.dataset.height) { const height = +this.dataset.height; - imageConverterPalette.querySelector(`div[data-color="${height}"]`).classList.add("hoveredColor"); + imageConverterPalette.querySelector(`div[data-color="${height}"]`).classList.add('hoveredColor'); colorsSelectValue.innerHTML = height; colorsSelectFriendly.innerHTML = getHeight(height); } - const color = this.getAttribute("data-color"); - viewbox.select("#heights").selectAll("polygon.selectedCell").classed("selectedCell", 0); - viewbox.select("#heights").selectAll("polygon[fill='" + color + "']").classed("selectedCell", 1); + const color = this.getAttribute('data-color'); + viewbox.select('#heights').selectAll('polygon.selectedCell').classed('selectedCell', 0); + viewbox + .select('#heights') + .selectAll("polygon[fill='" + color + "']") + .classed('selectedCell', 1); } function assignHeight() { const height = +this.dataset.color; - const rgb = color(1 - (height < 20 ? height-5 : height) / 100); - const selectedColor = imageConverter.querySelector("div.selectedColor"); + const rgb = color(1 - (height < 20 ? height - 5 : height) / 100); + const selectedColor = imageConverter.querySelector('div.selectedColor'); selectedColor.style.backgroundColor = rgb; - selectedColor.setAttribute("data-color", rgb); - selectedColor.setAttribute("data-height", height); + selectedColor.setAttribute('data-color', rgb); + selectedColor.setAttribute('data-height', height); - viewbox.select("#heights").selectAll(".selectedCell").each(function() { - this.setAttribute("fill", rgb); - this.setAttribute("data-height", height); - }); + viewbox + .select('#heights') + .selectAll('.selectedCell') + .each(function () { + this.setAttribute('fill', rgb); + this.setAttribute('data-height', height); + }); - if (selectedColor.parentNode.id === "colorsUnassigned") { + if (selectedColor.parentNode.id === 'colorsUnassigned') { colorsAssigned.appendChild(selectedColor); - colorsAssigned.style.display = "block"; + colorsAssigned.style.display = 'block'; - document.getElementById("colorsUnassignedNumber").innerHTML = colorsUnassigned.childElementCount - 2; - document.getElementById("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; + document.getElementById('colorsUnassignedNumber').innerHTML = colorsUnassigned.childElementCount - 2; + document.getElementById('colorsAssignedNumber').innerHTML = colorsAssigned.childElementCount - 2; } - } // auto assign color based on luminosity or hue function autoAssing(type) { - let unassigned = colorsUnassigned.querySelectorAll("div"); + let unassigned = colorsUnassigned.querySelectorAll('div'); if (!unassigned.length) { heightsFromImage(+convertColors.value); - unassigned = colorsUnassigned.querySelectorAll("div"); + unassigned = colorsUnassigned.querySelectorAll('div'); if (!unassigned.length) { - tip("No unassigned colors. Please load an image and click the button again", false, "error"); + tip('No unassigned colors. Please load an image and click the button again', false, 'error'); return; } } - const getHeightByHue = function(color) { + const getHeightByHue = function (color) { let hue = d3.hsl(color).h; if (hue > 300) hue -= 360; - if (hue > 170) return Math.abs(hue-250) / 3 |0; // water - return Math.abs(hue-250+20) / 3 |0; // land - } + if (hue > 170) return (Math.abs(hue - 250) / 3) | 0; // water + return (Math.abs(hue - 250 + 20) / 3) | 0; // land + }; - const getHeightByLum = function(color) { + const getHeightByLum = function (color) { let lum = d3.lab(color).l; - if (lum < 13) return lum / 13 * 20 |0; // water - return lum|0; // land - } + if (lum < 13) return ((lum / 13) * 20) | 0; // water + return lum | 0; // land + }; - const scheme = d3.range(101).map(i => getColor(i, color())); - const hues = scheme.map(rgb => d3.hsl(rgb).h|0); - const getHeightByScheme = function(color) { + const scheme = d3.range(101).map((i) => getColor(i, color())); + const hues = scheme.map((rgb) => d3.hsl(rgb).h | 0); + const getHeightByScheme = function (color) { let height = scheme.indexOf(color); if (height !== -1) return height; // exact match const hue = d3.hsl(color).h; const closest = hues.reduce((prev, curr) => (Math.abs(curr - hue) < Math.abs(prev - hue) ? curr : prev)); return hues.indexOf(closest); - } + }; const assinged = []; // store assigned heights - unassigned.forEach(el => { + unassigned.forEach((el) => { const clr = el.dataset.color; - const height = type === "hue" ? getHeightByHue(clr) : type === "lum" ? getHeightByLum(clr) : getHeightByScheme(clr); - const colorTo = color(1 - (height < 20 ? (height-5) / 100 : height / 100)); - viewbox.select("#heights").selectAll("polygon[fill='" + clr + "']").attr("fill", colorTo).attr("data-height", height); + const height = type === 'hue' ? getHeightByHue(clr) : type === 'lum' ? getHeightByLum(clr) : getHeightByScheme(clr); + const colorTo = color(1 - (height < 20 ? (height - 5) / 100 : height / 100)); + viewbox + .select('#heights') + .selectAll("polygon[fill='" + clr + "']") + .attr('fill', colorTo) + .attr('data-height', height); - if (assinged[height]) {el.remove(); return;} // if color is already added, remove it + if (assinged[height]) { + el.remove(); + return; + } // if color is already added, remove it el.style.backgroundColor = el.dataset.color = colorTo; el.dataset.height = height; colorsAssigned.appendChild(el); @@ -1178,16 +1273,15 @@ function editHeightmap() { // sort assigned colors by height Array.from(colorsAssigned.children) .sort((a, b) => +a.dataset.height - +b.dataset.height) - .forEach(line => colorsAssigned.appendChild(line)); + .forEach((line) => colorsAssigned.appendChild(line)); - colorsAssigned.style.display = "block"; - colorsUnassigned.style.display = "none"; - document.getElementById("colorsAssignedNumber").innerHTML = colorsAssigned.childElementCount - 2; + colorsAssigned.style.display = 'block'; + colorsUnassigned.style.display = 'none'; + document.getElementById('colorsAssignedNumber').innerHTML = colorsAssigned.childElementCount - 2; } function setConvertColorsNumber() { - prompt(`Please set maximum number of colors.
An actual number is usually lower and depends on color scheme`, - {default:+convertColors.value, step:1, min:3, max:255}, number => { + prompt(`Please set maximum number of colors.
An actual number is usually lower and depends on color scheme`, {default: +convertColors.value, step: 1, min: 3, max: 255}, (number) => { convertColors.value = number; heightsFromImage(number); }); @@ -1195,43 +1289,46 @@ function editHeightmap() { function setOverlayOpacity(v) { convertOverlay.value = convertOverlayNumber.value = v; - document.getElementById("canvas").style.opacity = v; + document.getElementById('canvas').style.opacity = v; } function applyConversion() { if (colorsAssigned.childElementCount < 3) { - tip("Please do the assignment first", false, "error"); + tip('Please do the assignment first', false, 'error'); return; } - viewbox.select("#heights").selectAll("polygon").each(function() { - const height = +this.dataset.height || 0; - const i = +this.id.slice(4); - grid.cells.h[i] = height; - }); + viewbox + .select('#heights') + .selectAll('polygon') + .each(function () { + const height = +this.dataset.height || 0; + const i = +this.id.slice(4); + grid.cells.h[i] = height; + }); - viewbox.select("#heights").selectAll("polygon").remove(); + viewbox.select('#heights').selectAll('polygon').remove(); updateHeightmap(); restoreImageConverterState(); } function cancelConversion() { restoreImageConverterState(); - viewbox.select("#heights").selectAll("polygon").remove(); - restoreHistory(edits.n-1); + viewbox.select('#heights').selectAll('polygon').remove(); + restoreHistory(edits.n - 1); } function restoreImageConverterState() { - const canvas = document.getElementById("canvas"); + const canvas = document.getElementById('canvas'); if (canvas) canvas.remove(); - d3.select("#imageConverter").selectAll("div.color-div").remove(); - colorsAssigned.style.display = "none"; - colorsUnassigned.style.display = "none"; + d3.select('#imageConverter').selectAll('div.color-div').remove(); + colorsAssigned.style.display = 'none'; + colorsUnassigned.style.display = 'none'; colorsSelectValue.innerHTML = colorsSelectFriendly.innerHTML = 0; - viewbox.style("cursor", "default").on(".drag", null); + viewbox.style('cursor', 'default').on('.drag', null); tip('Heightmap edit mode is active. Click on "Exit Customization" to finalize the heightmap', true); - $("#imageConverter").dialog("destroy"); + $('#imageConverter').dialog('destroy'); openBrushesPanel(); } @@ -1244,20 +1341,22 @@ function editHeightmap() { Click "Complete" to apply the conversion. Click "Close" to exit conversion mode and restore previous heightmap`; - $("#alert").dialog({resizable: false, title: "Close Image Converter", + $('#alert').dialog({ + resizable: false, + title: 'Close Image Converter', buttons: { - Cancel: function() { - $(this).dialog("close"); + Cancel: function () { + $(this).dialog('close'); }, - Complete: function() { - $(this).dialog("close"); + Complete: function () { + $(this).dialog('close'); applyConversion(); }, - Close: function() { - $(this).dialog("close"); + Close: function () { + $(this).dialog('close'); restoreImageConverterState(); - viewbox.select("#heights").selectAll("polygon").remove(); - restoreHistory(edits.n-1); + viewbox.select('#heights').selectAll('polygon').remove(); + restoreHistory(edits.n - 1); } } }); @@ -1265,57 +1364,56 @@ function editHeightmap() { } function toggleHeightmapPreview() { - if (document.getElementById("preview")) { - document.getElementById("preview").remove(); + if (document.getElementById('preview')) { + document.getElementById('preview').remove(); return; } - const preview = document.createElement("canvas"); - preview.id = "preview"; + const preview = document.createElement('canvas'); + preview.id = 'preview'; preview.width = grid.cellsX; preview.height = grid.cellsY; document.body.insertBefore(preview, optionsContainer); - preview.addEventListener("mouseover", () => tip("Heightmap preview. Click to download a screen-sized image")); - preview.addEventListener("click", downloadPreview); + preview.addEventListener('mouseover', () => tip('Heightmap preview. Click to download a screen-sized image')); + preview.addEventListener('click', downloadPreview); drawHeightmapPreview(); } function drawHeightmapPreview() { - const ctx = document.getElementById("preview").getContext("2d"); + const ctx = document.getElementById('preview').getContext('2d'); const imageData = ctx.createImageData(grid.cellsX, grid.cellsY); grid.cells.h.forEach((height, i) => { let h = height < 20 ? Math.max(height / 1.5, 0) : height; - const v = h / 100 * 255; - imageData.data[i*4] = v; - imageData.data[i*4 + 1] = v; - imageData.data[i*4 + 2] = v; - imageData.data[i*4 + 3] = 255; + const v = (h / 100) * 255; + imageData.data[i * 4] = v; + imageData.data[i * 4 + 1] = v; + imageData.data[i * 4 + 2] = v; + imageData.data[i * 4 + 3] = 255; }); ctx.putImageData(imageData, 0, 0); } function downloadPreview() { - const preview = document.getElementById("preview"); - const dataURL = preview.toDataURL("image/png"); + const preview = document.getElementById('preview'); + const dataURL = preview.toDataURL('image/png'); const img = new Image(); img.src = dataURL; - img.onload = function() { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); + img.onload = function () { + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); canvas.width = svgWidth; canvas.height = svgHeight; document.body.insertBefore(canvas, optionsContainer); ctx.drawImage(img, 0, 0, svgWidth, svgHeight); - const imgBig = canvas.toDataURL("image/png"); - const link = document.createElement("a"); - link.download = getFileName("Heightmap") + ".png"; + const imgBig = canvas.toDataURL('image/png'); + const link = document.createElement('a'); + link.download = getFileName('Heightmap') + '.png'; link.href = imgBig; link.click(); canvas.remove(); - } + }; } - } diff --git a/modules/ui/ice-editor.js b/modules/ui/ice-editor.js index f6648f48..68c412d1 100644 --- a/modules/ui/ice-editor.js +++ b/modules/ui/ice-editor.js @@ -1,19 +1,20 @@ -"use strict"; +'use strict'; function editIce() { if (customization) return; - closeDialogs(".stable"); - if (!layerIsOn("toggleIce")) toggleIce(); + closeDialogs('.stable'); + if (!layerIsOn('toggleIce')) toggleIce(); elSelected = d3.select(d3.event.target); - const type = elSelected.attr("type") ? "Glacier" : "Iceberg"; - document.getElementById("iceRandomize").style.display = type === "Glacier" ? "none" : "inline-block"; - document.getElementById("iceSize").style.display = type === "Glacier" ? "none" : "inline-block"; - if (type === "Iceberg") document.getElementById("iceSize").value = +elSelected.attr("size"); - ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement)); + const type = elSelected.attr('type') ? 'Glacier' : 'Iceberg'; + document.getElementById('iceRandomize').style.display = type === 'Glacier' ? 'none' : 'inline-block'; + document.getElementById('iceSize').style.display = type === 'Glacier' ? 'none' : 'inline-block'; + if (type === 'Iceberg') document.getElementById('iceSize').value = +elSelected.attr('size'); + ice.selectAll('*').classed('draggable', true).call(d3.drag().on('drag', dragElement)); - $("#iceEditor").dialog({ - title: "Edit "+type, resizable: false, - position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"}, + $('#iceEditor').dialog({ + title: 'Edit ' + type, + resizable: false, + position: {my: 'center top+60', at: 'top', of: d3.event, collision: 'fit'}, close: closeEditor }); @@ -21,41 +22,45 @@ function editIce() { modules.editIce = true; // add listeners - document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice")); - document.getElementById("iceRandomize").addEventListener("click", randomizeShape); - document.getElementById("iceSize").addEventListener("input", changeSize); - document.getElementById("iceNew").addEventListener("click", toggleAdd); - document.getElementById("iceRemove").addEventListener("click", removeIce); + document.getElementById('iceEditStyle').addEventListener('click', () => editStyle('ice')); + document.getElementById('iceRandomize').addEventListener('click', randomizeShape); + document.getElementById('iceSize').addEventListener('input', changeSize); + document.getElementById('iceNew').addEventListener('click', toggleAdd); + document.getElementById('iceRemove').addEventListener('click', removeIce); function randomizeShape() { - const c = grid.points[+elSelected.attr("cell")]; - const s = +elSelected.attr("size"); - const i = ra(grid.cells.i), cn = grid.points[i]; - const poly = getGridPolygon(i).map(p => [p[0]-cn[0], p[1]-cn[1]]); - const points = poly.map(p => [rn(c[0] + p[0] * s, 2), rn(c[1] + p[1] * s, 2)]); - elSelected.attr("points", points); + const c = grid.points[+elSelected.attr('cell')]; + const s = +elSelected.attr('size'); + const i = ra(grid.cells.i), + cn = grid.points[i]; + const poly = getGridPolygon(i).map((p) => [p[0] - cn[0], p[1] - cn[1]]); + const points = poly.map((p) => [rn(c[0] + p[0] * s, 2), rn(c[1] + p[1] * s, 2)]); + elSelected.attr('points', points); } function changeSize() { - const c = grid.points[+elSelected.attr("cell")]; - const s = +elSelected.attr("size"); - const flat = elSelected.attr("points").split(",").map(el => +el); + const c = grid.points[+elSelected.attr('cell')]; + const s = +elSelected.attr('size'); + const flat = elSelected + .attr('points') + .split(',') + .map((el) => +el); const pairs = []; - while (flat.length) pairs.push(flat.splice(0,2)); - const poly = pairs.map(p => [(p[0]-c[0]) / s, (p[1]-c[1]) / s]); + while (flat.length) pairs.push(flat.splice(0, 2)); + const poly = pairs.map((p) => [(p[0] - c[0]) / s, (p[1] - c[1]) / s]); const size = +this.value; - const points = poly.map(p => [rn(c[0] + p[0] * size, 2), rn(c[1] + p[1] * size, 2)]); - elSelected.attr("points", points).attr("size", size); + const points = poly.map((p) => [rn(c[0] + p[0] * size, 2), rn(c[1] + p[1] * size, 2)]); + elSelected.attr('points', points).attr('size', size); } function toggleAdd() { - document.getElementById("iceNew").classList.toggle("pressed"); - if (document.getElementById("iceNew").classList.contains("pressed")) { - viewbox.style("cursor", "crosshair").on("click", addIcebergOnClick); - tip("Click on map to create an iceberg. Hold Shift to add multiple", true); + document.getElementById('iceNew').classList.toggle('pressed'); + if (document.getElementById('iceNew').classList.contains('pressed')) { + viewbox.style('cursor', 'crosshair').on('click', addIcebergOnClick); + tip('Click on map to create an iceberg. Hold Shift to add multiple', true); } else { clearMainTip(); - viewbox.on("click", clicked).style("cursor", "default"); + viewbox.on('click', clicked).style('cursor', 'default'); } } @@ -63,43 +68,41 @@ function editIce() { const point = d3.mouse(this); const i = findGridCell(point[0], point[1]); const c = grid.points[i]; - const s = +document.getElementById("iceSize").value; + const s = +document.getElementById('iceSize').value; - const points = getGridPolygon(i).map(p => [(p[0] + (c[0]-p[0]) / s)|0, (p[1] + (c[1]-p[1]) / s)|0]); - const iceberg = ice.append("polygon").attr("points", points).attr("cell", i).attr("size", s); - iceberg.call(d3.drag().on("drag", dragElement)); + const points = getGridPolygon(i).map((p) => [(p[0] + (c[0] - p[0]) / s) | 0, (p[1] + (c[1] - p[1]) / s) | 0]); + const iceberg = ice.append('polygon').attr('points', points).attr('cell', i).attr('size', s); + iceberg.call(d3.drag().on('drag', dragElement)); if (d3.event.shiftKey === false) toggleAdd(); } function removeIce() { - const type = elSelected.attr("type") ? "Glacier" : "Iceberg"; - alertMessage.innerHTML = `Are you sure you want to remove the ${type}?`; - $("#alert").dialog({resizable: false, title: "Remove "+type, - buttons: { - Remove: function() { - $(this).dialog("close"); - elSelected.remove(); - $("#iceEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const type = elSelected.attr('type') ? 'Glacier' : 'Iceberg'; + + const message = `Are you sure you want to remove the ${type}?
This action cannot be reverted`; + const onConfirm = () => { + elSelected.remove(); + $('#iceEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove ' + type, message, confirm: 'Remove', onConfirm}); } function dragElement() { - const tr = parseTransform(this.getAttribute("transform")); - const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y; + const tr = parseTransform(this.getAttribute('transform')); + const dx = +tr[0] - d3.event.x, + dy = +tr[1] - d3.event.y; - d3.event.on("drag", function() { - const x = d3.event.x, y = d3.event.y; - this.setAttribute("transform", `translate(${(dx+x)},${(dy+y)})`); + d3.event.on('drag', function () { + const x = d3.event.x, + y = d3.event.y; + this.setAttribute('transform', `translate(${dx + x},${dy + y})`); }); } function closeEditor() { - ice.selectAll("*").classed("draggable", false).call(d3.drag().on("drag", null)); + ice.selectAll('*').classed('draggable', false).call(d3.drag().on('drag', null)); clearMainTip(); - iceNew.classList.remove("pressed"); + iceNew.classList.remove('pressed'); unselect(); } } diff --git a/modules/ui/labels-editor.js b/modules/ui/labels-editor.js index 18b1f8f6..956bbe8a 100644 --- a/modules/ui/labels-editor.js +++ b/modules/ui/labels-editor.js @@ -1,18 +1,20 @@ -"use strict"; +'use strict'; function editLabel() { if (customization) return; closeDialogs(); - if (!layerIsOn("toggleLabels")) toggleLabels(); + if (!layerIsOn('toggleLabels')) toggleLabels(); const tspan = d3.event.target; const textPath = tspan.parentNode; const text = textPath.parentNode; - elSelected = d3.select(text).call(d3.drag().on("start", dragLabel)).classed("draggable", true); - viewbox.on("touchmove mousemove", showEditorTips); + elSelected = d3.select(text).call(d3.drag().on('start', dragLabel)).classed('draggable', true); + viewbox.on('touchmove mousemove', showEditorTips); - $("#labelEditor").dialog({ - title: "Edit Label", resizable: false, width: fitContent(), - position: {my: "center top+10", at: "bottom", of: text, collision: "fit"}, + $('#labelEditor').dialog({ + title: 'Edit Label', + resizable: false, + width: fitContent(), + position: {my: 'center top+10', at: 'bottom', of: text, collision: 'fit'}, close: closeLabelEditor }); @@ -24,89 +26,99 @@ function editLabel() { modules.editLabel = true; // add listeners - document.getElementById("labelGroupShow").addEventListener("click", showGroupSection); - document.getElementById("labelGroupHide").addEventListener("click", hideGroupSection); - document.getElementById("labelGroupSelect").addEventListener("click", changeGroup); - document.getElementById("labelGroupInput").addEventListener("change", createNewGroup); - document.getElementById("labelGroupNew").addEventListener("click", toggleNewGroupInput); - document.getElementById("labelGroupRemove").addEventListener("click", removeLabelsGroup); + document.getElementById('labelGroupShow').addEventListener('click', showGroupSection); + document.getElementById('labelGroupHide').addEventListener('click', hideGroupSection); + document.getElementById('labelGroupSelect').addEventListener('click', changeGroup); + document.getElementById('labelGroupInput').addEventListener('change', createNewGroup); + document.getElementById('labelGroupNew').addEventListener('click', toggleNewGroupInput); + document.getElementById('labelGroupRemove').addEventListener('click', removeLabelsGroup); - document.getElementById("labelTextShow").addEventListener("click", showTextSection); - document.getElementById("labelTextHide").addEventListener("click", hideTextSection); - document.getElementById("labelText").addEventListener("input", changeText); - document.getElementById("labelTextRandom").addEventListener("click", generateRandomName); + document.getElementById('labelTextShow').addEventListener('click', showTextSection); + document.getElementById('labelTextHide').addEventListener('click', hideTextSection); + document.getElementById('labelText').addEventListener('input', changeText); + document.getElementById('labelTextRandom').addEventListener('click', generateRandomName); - document.getElementById("labelEditStyle").addEventListener("click", editGroupStyle); + document.getElementById('labelEditStyle').addEventListener('click', editGroupStyle); - document.getElementById("labelSizeShow").addEventListener("click", showSizeSection); - document.getElementById("labelSizeHide").addEventListener("click", hideSizeSection); - document.getElementById("labelStartOffset").addEventListener("input", changeStartOffset); - document.getElementById("labelRelativeSize").addEventListener("input", changeRelativeSize); + document.getElementById('labelSizeShow').addEventListener('click', showSizeSection); + document.getElementById('labelSizeHide').addEventListener('click', hideSizeSection); + document.getElementById('labelStartOffset').addEventListener('input', changeStartOffset); + document.getElementById('labelRelativeSize').addEventListener('input', changeRelativeSize); - document.getElementById("labelAlign").addEventListener("click", editLabelAlign); - document.getElementById("labelLegend").addEventListener("click", editLabelLegend); - document.getElementById("labelRemoveSingle").addEventListener("click", removeLabel); + document.getElementById('labelAlign').addEventListener('click', editLabelAlign); + document.getElementById('labelLegend').addEventListener('click', editLabelLegend); + document.getElementById('labelRemoveSingle').addEventListener('click', removeLabel); function showEditorTips() { showMainTip(); - if (d3.event.target.parentNode.parentNode.id === elSelected.attr("id")) tip("Drag to shift the label"); else - if (d3.event.target.parentNode.id === "controlPoints") { - if (d3.event.target.tagName === "circle") tip("Drag to move, click to delete the control point"); - if (d3.event.target.tagName === "path") tip("Click to add a control point"); + if (d3.event.target.parentNode.parentNode.id === elSelected.attr('id')) tip('Drag to shift the label'); + else if (d3.event.target.parentNode.id === 'controlPoints') { + if (d3.event.target.tagName === 'circle') tip('Drag to move, click to delete the control point'); + if (d3.event.target.tagName === 'path') tip('Click to add a control point'); } } function selectLabelGroup(text) { const group = text.parentNode.id; - const select = document.getElementById("labelGroupSelect"); + const select = document.getElementById('labelGroupSelect'); select.options.length = 0; // remove all options - labels.selectAll(":scope > g").each(function() { - if (this.id === "burgLabels") return; + labels.selectAll(':scope > g').each(function () { + if (this.id === 'burgLabels') return; select.options.add(new Option(this.id, this.id, false, this.id === group)); }); } function updateValues(textPath) { - document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")].map(tspan => tspan.textContent).join("|"); - document.getElementById("labelStartOffset").value = parseFloat(textPath.getAttribute("startOffset")); - document.getElementById("labelRelativeSize").value = parseFloat(textPath.getAttribute("font-size")); + document.getElementById('labelText').value = [...textPath.querySelectorAll('tspan')].map((tspan) => tspan.textContent).join('|'); + document.getElementById('labelStartOffset').value = parseFloat(textPath.getAttribute('startOffset')); + document.getElementById('labelRelativeSize').value = parseFloat(textPath.getAttribute('font-size')); } function drawControlPointsAndLine() { - debug.select("#controlPoints").remove(); - debug.append("g").attr("id", "controlPoints").attr("transform", elSelected.attr("transform")); - const path = document.getElementById("textPath_" + elSelected.attr("id")); - debug.select("#controlPoints").append("path").attr("d", path.getAttribute("d")).on("click", addInterimControlPoint); + debug.select('#controlPoints').remove(); + debug.append('g').attr('id', 'controlPoints').attr('transform', elSelected.attr('transform')); + const path = document.getElementById('textPath_' + elSelected.attr('id')); + debug.select('#controlPoints').append('path').attr('d', path.getAttribute('d')).on('click', addInterimControlPoint); const l = path.getTotalLength(); if (!l) return; const increment = l / Math.max(Math.ceil(l / 200), 2); - for (let i=0; i <= l; i += increment) {addControlPoint(path.getPointAtLength(i));} + for (let i = 0; i <= l; i += increment) { + addControlPoint(path.getPointAtLength(i)); + } } function addControlPoint(point) { - debug.select("#controlPoints").append("circle") - .attr("cx", point.x).attr("cy", point.y).attr("r", 2.5).attr("stroke-width", .8) - .call(d3.drag().on("drag", dragControlPoint)) - .on("click", clickControlPoint); + debug + .select('#controlPoints') + .append('circle') + .attr('cx', point.x) + .attr('cy', point.y) + .attr('r', 2.5) + .attr('stroke-width', 0.8) + .call(d3.drag().on('drag', dragControlPoint)) + .on('click', clickControlPoint); } function dragControlPoint() { - this.setAttribute("cx", d3.event.x); - this.setAttribute("cy", d3.event.y); + this.setAttribute('cx', d3.event.x); + this.setAttribute('cy', d3.event.y); redrawLabelPath(); } function redrawLabelPath() { - const path = document.getElementById("textPath_" + elSelected.attr("id")); + const path = document.getElementById('textPath_' + elSelected.attr('id')); lineGen.curve(d3.curveBundle.beta(1)); const points = []; - debug.select("#controlPoints").selectAll("circle").each(function() { - points.push([this.getAttribute("cx"), this.getAttribute("cy")]); - }); + debug + .select('#controlPoints') + .selectAll('circle') + .each(function () { + points.push([this.getAttribute('cx'), this.getAttribute('cy')]); + }); const d = round(lineGen(points)); - path.setAttribute("d", d); - debug.select("#controlPoints > path").attr("d", d); + path.setAttribute('d', d); + debug.select('#controlPoints > path').attr('d', d); } function clickControlPoint() { @@ -118,52 +130,63 @@ function editLabel() { const point = d3.mouse(this); const dists = []; - debug.select("#controlPoints").selectAll("circle").each(function() { - const x = +this.getAttribute("cx"); - const y = +this.getAttribute("cy"); - dists.push((point[0] - x) ** 2 + (point[1] - y) ** 2); - }); + debug + .select('#controlPoints') + .selectAll('circle') + .each(function () { + const x = +this.getAttribute('cx'); + const y = +this.getAttribute('cy'); + dists.push((point[0] - x) ** 2 + (point[1] - y) ** 2); + }); let index = dists.length; if (dists.length > 1) { - const sorted = dists.slice(0).sort((a, b) => a-b); + const sorted = dists.slice(0).sort((a, b) => a - b); const closest = dists.indexOf(sorted[0]); const next = dists.indexOf(sorted[1]); - if (closest <= next) index = closest+1; else index = next+1; + if (closest <= next) index = closest + 1; + else index = next + 1; } - const before = ":nth-child(" + (index + 2) + ")"; - debug.select("#controlPoints").insert("circle", before) - .attr("cx", point[0]).attr("cy", point[1]).attr("r", 2.5).attr("stroke-width", .8) - .call(d3.drag().on("drag", dragControlPoint)) - .on("click", clickControlPoint); + const before = ':nth-child(' + (index + 2) + ')'; + debug + .select('#controlPoints') + .insert('circle', before) + .attr('cx', point[0]) + .attr('cy', point[1]) + .attr('r', 2.5) + .attr('stroke-width', 0.8) + .call(d3.drag().on('drag', dragControlPoint)) + .on('click', clickControlPoint); - redrawLabelPath(); + redrawLabelPath(); } function dragLabel() { - const tr = parseTransform(elSelected.attr("transform")); - const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y; - - d3.event.on("drag", function() { - const x = d3.event.x, y = d3.event.y; - const transform = `translate(${(dx+x)},${(dy+y)})`; - elSelected.attr("transform", transform); - debug.select("#controlPoints").attr("transform", transform); + const tr = parseTransform(elSelected.attr('transform')); + const dx = +tr[0] - d3.event.x, + dy = +tr[1] - d3.event.y; + + d3.event.on('drag', function () { + const x = d3.event.x, + y = d3.event.y; + const transform = `translate(${dx + x},${dy + y})`; + elSelected.attr('transform', transform); + debug.select('#controlPoints').attr('transform', transform); }); } function showGroupSection() { - document.querySelectorAll("#labelEditor > button").forEach(el => el.style.display = "none"); - document.getElementById("labelGroupSection").style.display = "inline-block"; + document.querySelectorAll('#labelEditor > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('labelGroupSection').style.display = 'inline-block'; } function hideGroupSection() { - document.querySelectorAll("#labelEditor > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("labelGroupSection").style.display = "none"; - document.getElementById("labelGroupInput").style.display = "none"; - document.getElementById("labelGroupInput").value = ""; - document.getElementById("labelGroupSelect").style.display = "inline-block"; + document.querySelectorAll('#labelEditor > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('labelGroupSection').style.display = 'none'; + document.getElementById('labelGroupInput').style.display = 'none'; + document.getElementById('labelGroupInput').value = ''; + document.getElementById('labelGroupSelect').style.display = 'inline-block'; } function changeGroup() { @@ -171,179 +194,177 @@ function editLabel() { } function toggleNewGroupInput() { - if (labelGroupInput.style.display === "none") { - labelGroupInput.style.display = "inline-block"; + if (labelGroupInput.style.display === 'none') { + labelGroupInput.style.display = 'inline-block'; labelGroupInput.focus(); - labelGroupSelect.style.display = "none"; + labelGroupSelect.style.display = 'none'; } else { - labelGroupInput.style.display = "none"; - labelGroupSelect.style.display = "inline-block"; - } + labelGroupInput.style.display = 'none'; + labelGroupSelect.style.display = 'inline-block'; + } } function createNewGroup() { - if (!this.value) {tip("Please provide a valid group name"); return;} - const group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (!this.value) { + tip('Please provide a valid group name'); + return; + } + const group = this.value + .toLowerCase() + .replace(/ /g, '_') + .replace(/[^\w\s]/gi, ''); if (document.getElementById(group)) { - tip("Element with this id already exists. Please provide a unique name", false, "error"); + tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } if (Number.isFinite(+group.charAt(0))) { - tip("Group name should start with a letter", false, "error"); + tip('Group name should start with a letter', false, 'error'); return; } // just rename if only 1 element left const oldGroup = elSelected.node().parentNode; - if (oldGroup !== "states" && oldGroup !== "addedLabels" && oldGroup.childElementCount === 1) { - document.getElementById("labelGroupSelect").selectedOptions[0].remove(); - document.getElementById("labelGroupSelect").options.add(new Option(group, group, false, true)); + if (oldGroup !== 'states' && oldGroup !== 'addedLabels' && oldGroup.childElementCount === 1) { + document.getElementById('labelGroupSelect').selectedOptions[0].remove(); + document.getElementById('labelGroupSelect').options.add(new Option(group, group, false, true)); oldGroup.id = group; toggleNewGroupInput(); - document.getElementById("labelGroupInput").value = ""; + document.getElementById('labelGroupInput').value = ''; return; } const newGroup = elSelected.node().parentNode.cloneNode(false); - document.getElementById("labels").appendChild(newGroup); + document.getElementById('labels').appendChild(newGroup); newGroup.id = group; - document.getElementById("labelGroupSelect").options.add(new Option(group, group, false, true)); + document.getElementById('labelGroupSelect').options.add(new Option(group, group, false, true)); document.getElementById(group).appendChild(elSelected.node()); toggleNewGroupInput(); - document.getElementById("labelGroupInput").value = ""; + document.getElementById('labelGroupInput').value = ''; } function removeLabelsGroup() { const group = elSelected.node().parentNode.id; - const basic = group === "states" || group === "addedLabels"; + const basic = group === 'states' || group === 'addedLabels'; const count = elSelected.node().parentNode.childElementCount; - alertMessage.innerHTML = `Are you sure you want to remove - ${basic ? "all elements in the group" : "the entire label group"}? -

Labels to be removed: ${count}`; - $("#alert").dialog({resizable: false, title: "Remove route group", - buttons: { - Remove: function() { - $(this).dialog("close"); - $("#labelEditor").dialog("close"); - hideGroupSection(); - labels.select("#"+group).selectAll("text").each(function() { - document.getElementById("textPath_" + this.id).remove(); - this.remove(); - }); - if (!basic) labels.select("#"+group).remove(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + + const message = `Are you sure you want to remove ${basic ? 'all elements in the group' : 'the entire label group'}?

Labels to be removed: ${count}`; + const onConfirm = () => { + $('#labelEditor').dialog('close'); + hideGroupSection(); + labels + .select('#' + group) + .selectAll('text') + .each(function () { + document.getElementById('textPath_' + this.id).remove(); + this.remove(); + }); + if (!basic) labels.select('#' + group).remove(); + }; + confirmationDialog({title: 'Remove label group', message, confirm: 'Remove', onConfirm}); } - + function showTextSection() { - document.querySelectorAll("#labelEditor > button").forEach(el => el.style.display = "none"); - document.getElementById("labelTextSection").style.display = "inline-block"; + document.querySelectorAll('#labelEditor > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('labelTextSection').style.display = 'inline-block'; } function hideTextSection() { - document.querySelectorAll("#labelEditor > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("labelTextSection").style.display = "none"; + document.querySelectorAll('#labelEditor > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('labelTextSection').style.display = 'none'; } - - function changeText() { - const input = document.getElementById("labelText").value; - const el = elSelected.select("textPath").node(); - const example = d3.select(elSelected.node().parentNode) - .append("text").attr("x", 0).attr("x", 0) - .attr("font-size", el.getAttribute("font-size")).node(); - const lines = input.split("|"); + function changeText() { + const input = document.getElementById('labelText').value; + const el = elSelected.select('textPath').node(); + const example = d3.select(elSelected.node().parentNode).append('text').attr('x', 0).attr('x', 0).attr('font-size', el.getAttribute('font-size')).node(); + + const lines = input.split('|'); const top = (lines.length - 1) / -2; // y offset - const inner = lines.map((l, d) => { - example.innerHTML = l; - const left = example.getBBox().width / -2; // x offset - return `${l}`; - }).join(""); + const inner = lines + .map((l, d) => { + example.innerHTML = l; + const left = example.getBBox().width / -2; // x offset + return `${l}`; + }) + .join(''); el.innerHTML = inner; example.remove(); - if (elSelected.attr("id").slice(0,10) === "stateLabel") tip("Use States Editor to change an actual state name, not just a label", false, "warning"); + if (elSelected.attr('id').slice(0, 10) === 'stateLabel') tip('Use States Editor to change an actual state name, not just a label', false, 'warning'); } function generateRandomName() { - let name = ""; - if (elSelected.attr("id").slice(0,10) === "stateLabel") { - const id = +elSelected.attr("id").slice(10); + let name = ''; + if (elSelected.attr('id').slice(0, 10) === 'stateLabel') { + const id = +elSelected.attr('id').slice(10); const culture = pack.states[id].culture; - name = Names.getState(Names.getCulture(culture, 4, 7, ""), culture); + name = Names.getState(Names.getCulture(culture, 4, 7, ''), culture); } else { const box = elSelected.node().getBBox(); const cell = findCell((box.x + box.width) / 2, (box.y + box.height) / 2); const culture = pack.cells.culture[cell]; name = Names.getCulture(culture); } - document.getElementById("labelText").value = name; + document.getElementById('labelText').value = name; changeText(); } function editGroupStyle() { const g = elSelected.node().parentNode.id; - editStyle("labels", g); + editStyle('labels', g); } function showSizeSection() { - document.querySelectorAll("#labelEditor > button").forEach(el => el.style.display = "none"); - document.getElementById("labelSizeSection").style.display = "inline-block"; + document.querySelectorAll('#labelEditor > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('labelSizeSection').style.display = 'inline-block'; } function hideSizeSection() { - document.querySelectorAll("#labelEditor > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("labelSizeSection").style.display = "none"; + document.querySelectorAll('#labelEditor > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('labelSizeSection').style.display = 'none'; } function changeStartOffset() { - elSelected.select("textPath").attr("startOffset", this.value + "%"); - tip("Label offset: " + this.value + "%"); + elSelected.select('textPath').attr('startOffset', this.value + '%'); + tip('Label offset: ' + this.value + '%'); } function changeRelativeSize() { - elSelected.select("textPath").attr("font-size", this.value + "%"); - tip("Label relative size: " + this.value + "%"); + elSelected.select('textPath').attr('font-size', this.value + '%'); + tip('Label relative size: ' + this.value + '%'); changeText(); } function editLabelAlign() { const bbox = elSelected.node().getBBox(); const c = [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2]; - const path = defs.select("#textPath_" + elSelected.attr("id")); - path.attr("d", `M${c[0]-bbox.width},${c[1]}h${bbox.width*2}`); + const path = defs.select('#textPath_' + elSelected.attr('id')); + path.attr('d', `M${c[0] - bbox.width},${c[1]}h${bbox.width * 2}`); drawControlPointsAndLine(); } function editLabelLegend() { - const id = elSelected.attr("id"); + const id = elSelected.attr('id'); const name = elSelected.text(); editNotes(id, name); } function removeLabel() { - alertMessage.innerHTML = "Are you sure you want to remove the label?"; - $("#alert").dialog({resizable: false, title: "Remove label", - buttons: { - Remove: function() { - $(this).dialog("close"); - defs.select("#textPath_" + elSelected.attr("id")).remove(); - elSelected.remove(); - $("#labelEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the label?
This action cannot be reverted'; + const onConfirm = () => { + defs.select('#textPath_' + elSelected.attr('id')).remove(); + elSelected.remove(); + $('#labelEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove label', message, confirm: 'Remove', onConfirm}); } function closeLabelEditor() { - debug.select("#controlPoints").remove(); + debug.select('#controlPoints').remove(); unselect(); } } diff --git a/modules/ui/lakes-editor.js b/modules/ui/lakes-editor.js index 3f1e4d60..05b3c5d5 100644 --- a/modules/ui/lakes-editor.js +++ b/modules/ui/lakes-editor.js @@ -1,107 +1,126 @@ -"use strict"; +'use strict'; function editLake() { if (customization) return; - closeDialogs(".stable"); - if (layerIsOn("toggleCells")) toggleCells(); + closeDialogs('.stable'); + if (layerIsOn('toggleCells')) toggleCells(); - $("#lakeEditor").dialog({ - title: "Edit Lake", resizable: false, - position: {my: "center top+20", at: "top", of: d3.event, collision: "fit"}, + $('#lakeEditor').dialog({ + title: 'Edit Lake', + resizable: false, + position: {my: 'center top+20', at: 'top', of: d3.event, collision: 'fit'}, close: closeLakesEditor }); const node = d3.event.target; - debug.append("g").attr("id", "vertices"); + debug.append('g').attr('id', 'vertices'); elSelected = d3.select(node); updateLakeValues(); selectLakeGroup(node); drawLakeVertices(); - viewbox.on("touchmove mousemove", null); + viewbox.on('touchmove mousemove', null); if (modules.editLake) return; modules.editLake = true; // add listeners - document.getElementById("lakeName").addEventListener("input", changeName); - document.getElementById("lakeNameCulture").addEventListener("click", generateNameCulture); - document.getElementById("lakeNameRandom").addEventListener("click", generateNameRandom); + document.getElementById('lakeName').addEventListener('input', changeName); + document.getElementById('lakeNameCulture').addEventListener('click', generateNameCulture); + document.getElementById('lakeNameRandom').addEventListener('click', generateNameRandom); - document.getElementById("lakeGroup").addEventListener("change", changeLakeGroup); - document.getElementById("lakeGroupAdd").addEventListener("click", toggleNewGroupInput); - document.getElementById("lakeGroupName").addEventListener("change", createNewGroup); - document.getElementById("lakeGroupRemove").addEventListener("click", removeLakeGroup); + document.getElementById('lakeGroup').addEventListener('change', changeLakeGroup); + document.getElementById('lakeGroupAdd').addEventListener('click', toggleNewGroupInput); + document.getElementById('lakeGroupName').addEventListener('change', createNewGroup); + document.getElementById('lakeGroupRemove').addEventListener('click', removeLakeGroup); - document.getElementById("lakeEditStyle").addEventListener("click", editGroupStyle); - document.getElementById("lakeLegend").addEventListener("click", editLakeLegend); + document.getElementById('lakeEditStyle').addEventListener('click', editGroupStyle); + document.getElementById('lakeLegend').addEventListener('click', editLakeLegend); function getLake() { - const lakeId = +elSelected.attr("data-f"); - return pack.features.find(feature => feature.i === lakeId); + const lakeId = +elSelected.attr('data-f'); + return pack.features.find((feature) => feature.i === lakeId); } function updateLakeValues() { const cells = pack.cells; const l = getLake(); - document.getElementById("lakeName").value = l.name; + document.getElementById('lakeName').value = l.name; - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - document.getElementById("lakeArea").value = si(l.area * distanceScaleInput.value ** 2) + unit; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + document.getElementById('lakeArea').value = si(l.area * distanceScaleInput.value ** 2) + unit; - const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v])); - document.getElementById("lakeShoreLength").value = si(length * distanceScaleInput.value) + " " + distanceUnitInput.value; + const length = d3.polygonLength(l.vertices.map((v) => pack.vertices.p[v])); + document.getElementById('lakeShoreLength').value = si(length * distanceScaleInput.value) + ' ' + distanceUnitInput.value; - const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i)); - const heights = lakeCells.map(i => cells.h[i]); + const lakeCells = Array.from(cells.i.filter((i) => cells.f[i] === l.i)); + const heights = lakeCells.map((i) => cells.h[i]); - document.getElementById("lakeElevation").value = getHeight(l.height); - document.getElementById("lakeAvarageDepth").value = getHeight(d3.mean(heights), "abs"); - document.getElementById("lakeMaxDepth").value = getHeight(d3.min(heights), "abs"); + document.getElementById('lakeElevation').value = getHeight(l.height); + document.getElementById('lakeAvarageDepth').value = getHeight(d3.mean(heights), 'abs'); + document.getElementById('lakeMaxDepth').value = getHeight(d3.min(heights), 'abs'); - document.getElementById("lakeFlux").value = l.flux; - document.getElementById("lakeEvaporation").value = l.evaporation; + document.getElementById('lakeFlux').value = l.flux; + document.getElementById('lakeEvaporation').value = l.evaporation; - const inlets = l.inlets && l.inlets.map(inlet => pack.rivers.find(river => river.i === inlet)?.name); - const outlet = l.outlet ? pack.rivers.find(river => river.i === l.outlet)?.name : "no"; - document.getElementById("lakeInlets").value = inlets ? inlets.length : "no"; - document.getElementById("lakeInlets").title = inlets ? inlets.join(", ") : ""; - document.getElementById("lakeOutlet").value = outlet; + const inlets = l.inlets && l.inlets.map((inlet) => pack.rivers.find((river) => river.i === inlet)?.name); + const outlet = l.outlet ? pack.rivers.find((river) => river.i === l.outlet)?.name : 'no'; + document.getElementById('lakeInlets').value = inlets ? inlets.length : 'no'; + document.getElementById('lakeInlets').title = inlets ? inlets.join(', ') : ''; + document.getElementById('lakeOutlet').value = outlet; } function drawLakeVertices() { const v = getLake().vertices; // lake outer vertices - const c = [... new Set(v.map(v => pack.vertices.c[v]).flat())]; - debug.select("#vertices").selectAll("polygon").data(c).enter().append("polygon") - .attr("points", d => getPackPolygon(d)).attr("data-c", d => d); + const c = [...new Set(v.map((v) => pack.vertices.c[v]).flat())]; + debug + .select('#vertices') + .selectAll('polygon') + .data(c) + .enter() + .append('polygon') + .attr('points', (d) => getPackPolygon(d)) + .attr('data-c', (d) => d); - debug.select("#vertices").selectAll("circle").data(v).enter().append("circle") - .attr("cx", d => pack.vertices.p[d][0]).attr("cy", d => pack.vertices.p[d][1]) - .attr("r", .4).attr("data-v", d => d).call(d3.drag().on("drag", dragVertex)) - .on("mousemove", () => tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights")); + debug + .select('#vertices') + .selectAll('circle') + .data(v) + .enter() + .append('circle') + .attr('cx', (d) => pack.vertices.p[d][0]) + .attr('cy', (d) => pack.vertices.p[d][1]) + .attr('r', 0.4) + .attr('data-v', (d) => d) + .call(d3.drag().on('drag', dragVertex)) + .on('mousemove', () => tip('Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights')); } function dragVertex() { - const x = rn(d3.event.x, 2), y = rn(d3.event.y, 2); - this.setAttribute("cx", x); - this.setAttribute("cy", y); + const x = rn(d3.event.x, 2), + y = rn(d3.event.y, 2); + this.setAttribute('cx', x); + this.setAttribute('cy', y); const v = +this.dataset.v; pack.vertices.p[v] = [x, y]; - debug.select("#vertices").selectAll("polygon").attr("points", d => getPackPolygon(d)); + debug + .select('#vertices') + .selectAll('polygon') + .attr('points', (d) => getPackPolygon(d)); redrawLake(); } function redrawLake() { lineGen.curve(d3.curveBasisClosed); const feature = getLake(); - const points = feature.vertices.map(v => pack.vertices.p[v]); + const points = feature.vertices.map((v) => pack.vertices.p[v]); const d = round(lineGen(points)); - elSelected.attr("d", d); - defs.select("mask#land > path#land_"+feature.i).attr("d", d); // update land mask + elSelected.attr('d', d); + defs.select('mask#land > path#land_' + feature.i).attr('d', d); // update land mask - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; feature.area = Math.abs(d3.polygonArea(points)); - document.getElementById("lakeArea").value = si(feature.area * distanceScaleInput.value ** 2) + unit; + document.getElementById('lakeArea').value = si(feature.area * distanceScaleInput.value ** 2) + unit; } function changeName() { @@ -115,15 +134,15 @@ function editLake() { function generateNameRandom() { const lake = getLake(); - lake.name = lakeName.value = Names.getBase(rand(nameBases.length-1)); + lake.name = lakeName.value = Names.getBase(rand(nameBases.length - 1)); } function selectLakeGroup(node) { const group = node.parentNode.id; - const select = document.getElementById("lakeGroup"); + const select = document.getElementById('lakeGroup'); select.options.length = 0; // remove all options - lakes.selectAll("g").each(function() { + lakes.selectAll('g').each(function () { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); } @@ -134,93 +153,93 @@ function editLake() { } function toggleNewGroupInput() { - if (lakeGroupName.style.display === "none") { - lakeGroupName.style.display = "inline-block"; + if (lakeGroupName.style.display === 'none') { + lakeGroupName.style.display = 'inline-block'; lakeGroupName.focus(); - lakeGroup.style.display = "none"; + lakeGroup.style.display = 'none'; } else { - lakeGroupName.style.display = "none"; - lakeGroup.style.display = "inline-block"; - } + lakeGroupName.style.display = 'none'; + lakeGroup.style.display = 'inline-block'; + } } function createNewGroup() { - if (!this.value) {tip("Please provide a valid group name"); return;} - const group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (!this.value) { + tip('Please provide a valid group name'); + return; + } + const group = this.value + .toLowerCase() + .replace(/ /g, '_') + .replace(/[^\w\s]/gi, ''); if (document.getElementById(group)) { - tip("Element with this id already exists. Please provide a unique name", false, "error"); + tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } if (Number.isFinite(+group.charAt(0))) { - tip("Group name should start with a letter", false, "error"); + tip('Group name should start with a letter', false, 'error'); return; } // just rename if only 1 element left const oldGroup = elSelected.node().parentNode; - const basic = ["freshwater", "salt", "sinkhole", "frozen", "lava", "dry"].includes(oldGroup.id); + const basic = ['freshwater', 'salt', 'sinkhole', 'frozen', 'lava', 'dry'].includes(oldGroup.id); if (!basic && oldGroup.childElementCount === 1) { - document.getElementById("lakeGroup").selectedOptions[0].remove(); - document.getElementById("lakeGroup").options.add(new Option(group, group, false, true)); + document.getElementById('lakeGroup').selectedOptions[0].remove(); + document.getElementById('lakeGroup').options.add(new Option(group, group, false, true)); oldGroup.id = group; toggleNewGroupInput(); - document.getElementById("lakeGroupName").value = ""; + document.getElementById('lakeGroupName').value = ''; return; } // create a new group const newGroup = elSelected.node().parentNode.cloneNode(false); - document.getElementById("lakes").appendChild(newGroup); + document.getElementById('lakes').appendChild(newGroup); newGroup.id = group; - document.getElementById("lakeGroup").options.add(new Option(group, group, false, true)); + document.getElementById('lakeGroup').options.add(new Option(group, group, false, true)); document.getElementById(group).appendChild(elSelected.node()); toggleNewGroupInput(); - document.getElementById("lakeGroupName").value = ""; + document.getElementById('lakeGroupName').value = ''; } - + function removeLakeGroup() { const group = elSelected.node().parentNode.id; - if (["freshwater", "salt", "sinkhole", "frozen", "lava", "dry"].includes(group)) { - tip("This is one of the default groups, it cannot be removed", false, "error"); + if (['freshwater', 'salt', 'sinkhole', 'frozen', 'lava', 'dry'].includes(group)) { + tip('This is one of the default groups, it cannot be removed', false, 'error'); return; } const count = elSelected.node().parentNode.childElementCount; - alertMessage.innerHTML = `Are you sure you want to remove the group? - All lakes of the group (${count}) will be turned into Freshwater`; - $("#alert").dialog({resizable: false, title: "Remove lake group", width:"26em", - buttons: { - Remove: function() { - $(this).dialog("close"); - const freshwater = document.getElementById("freshwater"); - const groupEl = document.getElementById(group); - while (groupEl.childNodes.length) { - freshwater.appendChild(groupEl.childNodes[0]); - } - groupEl.remove(); - document.getElementById("lakeGroup").selectedOptions[0].remove(); - document.getElementById("lakeGroup").value = "freshwater"; - }, - Cancel: function() {$(this).dialog("close");} + const message = `Are you sure you want to remove the group?
All lakes of the group (${count}) will be turned into freshwater`; + const onConfirm = () => { + const freshwater = document.getElementById('freshwater'); + const groupEl = document.getElementById(group); + while (groupEl.childNodes.length) { + freshwater.appendChild(groupEl.childNodes[0]); } - }); + groupEl.remove(); + document.getElementById('lakeGroup').selectedOptions[0].remove(); + document.getElementById('lakeGroup').value = 'freshwater'; + }; + confirmationDialog({title: 'Remove lake group', message, confirm: 'Remove', onConfirm}); } function editGroupStyle() { const g = elSelected.node().parentNode.id; - editStyle("lakes", g); + editStyle('lakes', g); } function editLakeLegend() { - const id = elSelected.attr("id"); - editNotes(id, getLake().name + " " + lakeGroup.value + " lake"); + const id = elSelected.attr('id'); + editNotes(id, getLake().name + ' ' + lakeGroup.value + ' lake'); } function closeLakesEditor() { - debug.select("#vertices").remove(); + debug.select('#vertices').remove(); unselect(); } -} \ No newline at end of file +} diff --git a/modules/ui/markers-editor.js b/modules/ui/markers-editor.js index c1ea19f6..cc6fea78 100644 --- a/modules/ui/markers-editor.js +++ b/modules/ui/markers-editor.js @@ -1,130 +1,137 @@ -"use strict"; +'use strict'; function editMarker() { if (customization) return; - closeDialogs("#markerEditor, .stable"); - $("#markerEditor").dialog(); + closeDialogs('#markerEditor, .stable'); + $('#markerEditor').dialog(); - elSelected = d3.select(d3.event.target).call(d3.drag().on("start", dragMarker)).classed("draggable", true); + elSelected = d3.select(d3.event.target).call(d3.drag().on('start', dragMarker)).classed('draggable', true); updateInputs(); if (modules.editMarker) return; modules.editMarker = true; - $("#markerEditor").dialog({ - title: "Edit Marker", resizable: false, - position: {my: "center top+30", at: "bottom", of: d3.event, collision: "fit"}, + $('#markerEditor').dialog({ + title: 'Edit Marker', + resizable: false, + position: {my: 'center top+30', at: 'bottom', of: d3.event, collision: 'fit'}, close: closeMarkerEditor }); // add listeners - document.getElementById("markerGroup").addEventListener("click", toggleGroupSection); - document.getElementById("markerAddGroup").addEventListener("click", toggleGroupInput); - document.getElementById("markerSelectGroup").addEventListener("change", changeGroup); - document.getElementById("markerInputGroup").addEventListener("change", createGroup); - document.getElementById("markerRemoveGroup").addEventListener("click", removeGroup); + document.getElementById('markerGroup').addEventListener('click', toggleGroupSection); + document.getElementById('markerAddGroup').addEventListener('click', toggleGroupInput); + document.getElementById('markerSelectGroup').addEventListener('change', changeGroup); + document.getElementById('markerInputGroup').addEventListener('change', createGroup); + document.getElementById('markerRemoveGroup').addEventListener('click', removeGroup); - document.getElementById("markerIcon").addEventListener("click", toggleIconSection); - document.getElementById("markerIconSize").addEventListener("input", changeIconSize); - document.getElementById("markerIconShiftX").addEventListener("input", changeIconShiftX); - document.getElementById("markerIconShiftY").addEventListener("input", changeIconShiftY); - document.getElementById("markerIconSelect").addEventListener("click", selectMarkerIcon); + document.getElementById('markerIcon').addEventListener('click', toggleIconSection); + document.getElementById('markerIconSize').addEventListener('input', changeIconSize); + document.getElementById('markerIconShiftX').addEventListener('input', changeIconShiftX); + document.getElementById('markerIconShiftY').addEventListener('input', changeIconShiftY); + document.getElementById('markerIconSelect').addEventListener('click', selectMarkerIcon); - document.getElementById("markerStyle").addEventListener("click", toggleStyleSection); - document.getElementById("markerSize").addEventListener("input", changeMarkerSize); - document.getElementById("markerBaseStroke").addEventListener("input", changePinStroke); - document.getElementById("markerBaseFill").addEventListener("input", changePinFill); - document.getElementById("markerIconStrokeWidth").addEventListener("input", changeIconStrokeWidth); - document.getElementById("markerIconStroke").addEventListener("input", changeIconStroke); - document.getElementById("markerIconFill").addEventListener("input", changeIconFill); + document.getElementById('markerStyle').addEventListener('click', toggleStyleSection); + document.getElementById('markerSize').addEventListener('input', changeMarkerSize); + document.getElementById('markerBaseStroke').addEventListener('input', changePinStroke); + document.getElementById('markerBaseFill').addEventListener('input', changePinFill); + document.getElementById('markerIconStrokeWidth').addEventListener('input', changeIconStrokeWidth); + document.getElementById('markerIconStroke').addEventListener('input', changeIconStroke); + document.getElementById('markerIconFill').addEventListener('input', changeIconFill); - document.getElementById("markerToggleBubble").addEventListener("click", togglePinVisibility); - document.getElementById("markerLegendButton").addEventListener("click", editMarkerLegend); - document.getElementById("markerAdd").addEventListener("click", toggleAddMarker); - document.getElementById("markerRemove").addEventListener("click", removeMarker); + document.getElementById('markerToggleBubble').addEventListener('click', togglePinVisibility); + document.getElementById('markerLegendButton').addEventListener('click', editMarkerLegend); + document.getElementById('markerAdd').addEventListener('click', toggleAddMarker); + document.getElementById('markerRemove').addEventListener('click', removeMarker); updateGroupOptions(); function dragMarker() { - const tr = parseTransform(this.getAttribute("transform")); - const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y; - - d3.event.on("drag", function() { - const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`; - this.setAttribute("transform", transform); + const tr = parseTransform(this.getAttribute('transform')); + const x = +tr[0] - d3.event.x, + y = +tr[1] - d3.event.y; + + d3.event.on('drag', function () { + const transform = `translate(${x + d3.event.x},${y + d3.event.y})`; + this.setAttribute('transform', transform); }); } function updateInputs() { - const id = elSelected.attr("data-id"); - const symbol = d3.select("#defs-markers").select(id); - const icon = symbol.select("text"); + const id = elSelected.attr('data-id'); + const symbol = d3.select('#defs-markers').select(id); + const icon = symbol.select('text'); markerSelectGroup.value = id.slice(1); - markerIconSize.value = parseFloat(icon.attr("font-size")); - markerIconShiftX.value = parseFloat(icon.attr("x")); - markerIconShiftY.value = parseFloat(icon.attr("y")); + markerIconSize.value = parseFloat(icon.attr('font-size')); + markerIconShiftX.value = parseFloat(icon.attr('x')); + markerIconShiftY.value = parseFloat(icon.attr('y')); - markerSize.value = elSelected.attr("data-size"); - markerBaseStroke.value = symbol.select("path").attr("fill"); - markerBaseFill.value = symbol.select("circle").attr("fill"); + markerSize.value = elSelected.attr('data-size'); + markerBaseStroke.value = symbol.select('path').attr('fill'); + markerBaseFill.value = symbol.select('circle').attr('fill'); - markerIconStrokeWidth.value = icon.attr("stroke-width"); - markerIconStroke.value = icon.attr("stroke"); - markerIconFill.value = icon.attr("fill"); + markerIconStrokeWidth.value = icon.attr('stroke-width'); + markerIconStroke.value = icon.attr('stroke'); + markerIconFill.value = icon.attr('fill'); - markerToggleBubble.className = symbol.select("circle").attr("opacity") === "0" ? "icon-info" : "icon-info-circled"; + markerToggleBubble.className = symbol.select('circle').attr('opacity') === '0' ? 'icon-info' : 'icon-info-circled'; markerIconSelect.innerHTML = icon.text(); } function toggleGroupSection() { - if (markerGroupSection.style.display === "inline-block") { - markerEditor.querySelectorAll("button:not(#markerGroup)").forEach(b => b.style.display = "inline-block"); - markerGroupSection.style.display = "none"; + if (markerGroupSection.style.display === 'inline-block') { + markerEditor.querySelectorAll('button:not(#markerGroup)').forEach((b) => (b.style.display = 'inline-block')); + markerGroupSection.style.display = 'none'; } else { - markerEditor.querySelectorAll("button:not(#markerGroup)").forEach(b => b.style.display = "none"); - markerGroupSection.style.display = "inline-block"; + markerEditor.querySelectorAll('button:not(#markerGroup)').forEach((b) => (b.style.display = 'none')); + markerGroupSection.style.display = 'inline-block'; } } function updateGroupOptions() { - markerSelectGroup.innerHTML = ""; - d3.select("#defs-markers").selectAll("symbol").each(function() { - markerSelectGroup.options.add(new Option(this.id, this.id)); - }); - markerSelectGroup.value = elSelected.attr("data-id").slice(1); + markerSelectGroup.innerHTML = ''; + d3.select('#defs-markers') + .selectAll('symbol') + .each(function () { + markerSelectGroup.options.add(new Option(this.id, this.id)); + }); + markerSelectGroup.value = elSelected.attr('data-id').slice(1); } function toggleGroupInput() { - if (markerInputGroup.style.display === "inline-block") { - markerSelectGroup.style.display = "inline-block"; - markerInputGroup.style.display = "none"; + if (markerInputGroup.style.display === 'inline-block') { + markerSelectGroup.style.display = 'inline-block'; + markerInputGroup.style.display = 'none'; } else { - markerSelectGroup.style.display = "none"; - markerInputGroup.style.display = "inline-block"; + markerSelectGroup.style.display = 'none'; + markerInputGroup.style.display = 'inline-block'; markerInputGroup.focus(); } } function changeGroup() { - elSelected.attr("xlink:href", "#"+this.value); - elSelected.attr("data-id", "#"+this.value); + elSelected.attr('xlink:href', '#' + this.value); + elSelected.attr('data-id', '#' + this.value); } function createGroup() { - let newGroup = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); - if (Number.isFinite(+newGroup.charAt(0))) newGroup = "m" + newGroup; + let newGroup = this.value + .toLowerCase() + .replace(/ /g, '_') + .replace(/[^\w\s]/gi, ''); + if (Number.isFinite(+newGroup.charAt(0))) newGroup = 'm' + newGroup; if (document.getElementById(newGroup)) { - tip("Element with this id already exists. Please provide a unique name", false, "error"); + tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } - markerInputGroup.value = ""; + markerInputGroup.value = ''; // clone old group assigning new id - const id = elSelected.attr("data-id"); - const clone = d3.select("#defs-markers").select(id).node().cloneNode(true); + const id = elSelected.attr('data-id'); + const clone = d3.select('#defs-markers').select(id).node().cloneNode(true); clone.id = newGroup; - document.getElementById("defs-markers").insertBefore(clone, null); - elSelected.attr("xlink:href", "#"+newGroup).attr("data-id", "#"+newGroup); + document.getElementById('defs-markers').insertBefore(clone, null); + elSelected.attr('xlink:href', '#' + newGroup).attr('data-id', '#' + newGroup); // select new group markerSelectGroup.options.add(new Option(newGroup, newGroup, false, true)); @@ -132,156 +139,157 @@ function editMarker() { } function removeGroup() { - const id = elSelected.attr("data-id"); - const used = document.querySelectorAll("use[data-id='"+id+"']"); - const count = used.length === 1 ? "1 element" : used.length + " elements"; - alertMessage.innerHTML = "Are you sure you want to remove all markers of that type (" + count + ")?"; + const id = elSelected.attr('data-id'); + const used = document.querySelectorAll("use[data-id='" + id + "']"); - $("#alert").dialog({resizable: false, title: "Remove marker type", - buttons: { - Remove: function() { - $(this).dialog("close"); - if (id !== "#marker0") d3.select("#defs-markers").select(id).remove(); - used.forEach(e => { - const index = notes.findIndex(n => n.id === e.id); - if (index != -1) notes.splice(index, 1); - e.remove(); - }); - updateGroupOptions(); - updateGroupOptions(); - $("#markerEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const count = used.length === 1 ? '1 element' : used.length + ' elements'; + const message = `Are you sure you want to remove all markers of that type (${count})?
This action cannot be reverted`; + const onConfirm = () => { + if (id !== '#marker0') d3.select('#defs-markers').select(id).remove(); + used.forEach((e) => { + const index = notes.findIndex((n) => n.id === e.id); + if (index != -1) notes.splice(index, 1); + e.remove(); + }); + updateGroupOptions(); + updateGroupOptions(); + $('#markerEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove marker type', message, confirm: 'Remove', onConfirm}); } function toggleIconSection() { - if (markerIconSection.style.display === "inline-block") { - markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => b.style.display = "inline-block"); - markerIconSection.style.display = "none"; - markerIconSelect.style.display = "none"; + if (markerIconSection.style.display === 'inline-block') { + markerEditor.querySelectorAll('button:not(#markerIcon)').forEach((b) => (b.style.display = 'inline-block')); + markerIconSection.style.display = 'none'; + markerIconSelect.style.display = 'none'; } else { - markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => b.style.display = "none"); - markerIconSection.style.display = "inline-block"; - markerIconSelect.style.display = "inline-block"; + markerEditor.querySelectorAll('button:not(#markerIcon)').forEach((b) => (b.style.display = 'none')); + markerIconSection.style.display = 'inline-block'; + markerIconSelect.style.display = 'inline-block'; } } function selectMarkerIcon() { - selectIcon(this.innerHTML, v => { + selectIcon(this.innerHTML, (v) => { this.innerHTML = v; - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").text(v); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers').select(id).select('text').text(v); }); } function changeIconSize() { - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").attr("font-size", this.value + "px"); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers') + .select(id) + .select('text') + .attr('font-size', this.value + 'px'); } function changeIconShiftX() { - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").attr("x", this.value + "%"); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers') + .select(id) + .select('text') + .attr('x', this.value + '%'); } function changeIconShiftY() { - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").attr("y", this.value + "%"); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers') + .select(id) + .select('text') + .attr('y', this.value + '%'); } function toggleStyleSection() { - if (markerStyleSection.style.display === "inline-block") { - markerEditor.querySelectorAll("button:not(#markerStyle)").forEach(b => b.style.display = "inline-block"); - markerStyleSection.style.display = "none"; + if (markerStyleSection.style.display === 'inline-block') { + markerEditor.querySelectorAll('button:not(#markerStyle)').forEach((b) => (b.style.display = 'inline-block')); + markerStyleSection.style.display = 'none'; } else { - markerEditor.querySelectorAll("button:not(#markerStyle)").forEach(b => b.style.display = "none"); - markerStyleSection.style.display = "inline-block"; + markerEditor.querySelectorAll('button:not(#markerStyle)').forEach((b) => (b.style.display = 'none')); + markerStyleSection.style.display = 'inline-block'; } } function changeMarkerSize() { - const id = elSelected.attr("data-id"); - document.querySelectorAll("use[data-id='"+id+"']").forEach(e => { - const x = +e.dataset.x, y = +e.dataset.y; - const desired = e.dataset.size = +markerSize.value; + const id = elSelected.attr('data-id'); + document.querySelectorAll("use[data-id='" + id + "']").forEach((e) => { + const x = +e.dataset.x, + y = +e.dataset.y; + const desired = (e.dataset.size = +markerSize.value); const size = Math.max(desired * 5 + 25 / scale, 1); - e.setAttribute("x", x - size / 2); - e.setAttribute("y", y - size / 2); - e.setAttribute("width", size); - e.setAttribute("height", size); + e.setAttribute('x', x - size / 2); + e.setAttribute('y', y - size / 2); + e.setAttribute('width', size); + e.setAttribute('height', size); }); invokeActiveZooming(); } function changePinStroke() { - const id = elSelected.attr("data-id"); - d3.select(id).select("path").attr("fill", this.value); - d3.select(id).select("circle").attr("stroke", this.value); + const id = elSelected.attr('data-id'); + d3.select(id).select('path').attr('fill', this.value); + d3.select(id).select('circle').attr('stroke', this.value); } function changePinFill() { - const id = elSelected.attr("data-id"); - d3.select(id).select("circle").attr("fill", this.value); + const id = elSelected.attr('data-id'); + d3.select(id).select('circle').attr('fill', this.value); } function changeIconStrokeWidth() { - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").attr("stroke-width", this.value); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers').select(id).select('text').attr('stroke-width', this.value); } function changeIconStroke() { - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").attr("stroke", this.value); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers').select(id).select('text').attr('stroke', this.value); } function changeIconFill() { - const id = elSelected.attr("data-id"); - d3.select("#defs-markers").select(id).select("text").attr("fill", this.value); + const id = elSelected.attr('data-id'); + d3.select('#defs-markers').select(id).select('text').attr('fill', this.value); } function togglePinVisibility() { - const id = elSelected.attr("data-id"); + const id = elSelected.attr('data-id'); let show = 1; - if (this.className === "icon-info-circled") {this.className = "icon-info"; show = 0; } - else this.className = "icon-info-circled"; - d3.select(id).select("circle").attr("opacity", show); - d3.select(id).select("path").attr("opacity", show); + if (this.className === 'icon-info-circled') { + this.className = 'icon-info'; + show = 0; + } else this.className = 'icon-info-circled'; + d3.select(id).select('circle').attr('opacity', show); + d3.select(id).select('path').attr('opacity', show); } function editMarkerLegend() { - const id = elSelected.attr("id"); + const id = elSelected.attr('id'); editNotes(id, id); } function toggleAddMarker() { - document.getElementById("addMarker").click(); + document.getElementById('addMarker').click(); } function removeMarker() { - alertMessage.innerHTML = "Are you sure you want to remove the marker?"; - $("#alert").dialog({resizable: false, title: "Remove marker", - buttons: { - Remove: function() { - $(this).dialog("close"); - const index = notes.findIndex(n => n.id === elSelected.attr("id")); - if (index != -1) notes.splice(index, 1); - elSelected.remove(); - $("#markerEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the marker?
This action cannot be reverted'; + const onConfirm = () => { + const index = notes.findIndex((n) => n.id === elSelected.attr('id')); + if (index != -1) notes.splice(index, 1); + elSelected.remove(); + $('#markerEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove marker', message, confirm: 'Remove', onConfirm}); } function closeMarkerEditor() { unselect(); - if (addMarker.classList.contains("pressed")) addMarker.classList.remove("pressed"); - if (markerAdd.classList.contains("pressed")) markerAdd.classList.remove("pressed"); + if (addMarker.classList.contains('pressed')) addMarker.classList.remove('pressed'); + if (markerAdd.classList.contains('pressed')) markerAdd.classList.remove('pressed'); restoreDefaultEvents(); clearMainTip(); } } - diff --git a/modules/ui/military-overview.js b/modules/ui/military-overview.js index cc1ff082..fd7cdc13 100644 --- a/modules/ui/military-overview.js +++ b/modules/ui/military-overview.js @@ -1,71 +1,79 @@ -"use strict"; +'use strict'; function overviewMilitary() { if (customization) return; - closeDialogs("#militaryOverview, .stable"); - if (!layerIsOn("toggleStates")) toggleStates(); - if (!layerIsOn("toggleBorders")) toggleBorders(); - if (!layerIsOn("toggleMilitary")) toggleMilitary(); + closeDialogs('#militaryOverview, .stable'); + if (!layerIsOn('toggleStates')) toggleStates(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + if (!layerIsOn('toggleMilitary')) toggleMilitary(); - const body = document.getElementById("militaryBody"); + const body = document.getElementById('militaryBody'); addLines(); - $("#militaryOverview").dialog(); + $('#militaryOverview').dialog(); if (modules.overviewMilitary) return; modules.overviewMilitary = true; updateHeaders(); - $("#militaryOverview").dialog({ - title: "Military Overview", resizable: false, width: fitContent(), - position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} + $('#militaryOverview').dialog({ + title: 'Military Overview', + resizable: false, + width: fitContent(), + position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'} }); // add listeners - document.getElementById("militaryOverviewRefresh").addEventListener("click", addLines); - document.getElementById("militaryPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("militaryOptionsButton").addEventListener("click", militaryCustomize); - document.getElementById("militaryRegimentsList").addEventListener("click", () => overviewRegiments(-1)); - document.getElementById("militaryOverviewRecalculate").addEventListener("click", militaryRecalculate); - document.getElementById("militaryExport").addEventListener("click", downloadMilitaryData); - document.getElementById("militaryWiki").addEventListener("click", () => wiki("Military-Forces")); + document.getElementById('militaryOverviewRefresh').addEventListener('click', addLines); + document.getElementById('militaryPercentage').addEventListener('click', togglePercentageMode); + document.getElementById('militaryOptionsButton').addEventListener('click', militaryCustomize); + document.getElementById('militaryRegimentsList').addEventListener('click', () => overviewRegiments(-1)); + document.getElementById('militaryOverviewRecalculate').addEventListener('click', militaryRecalculate); + document.getElementById('militaryExport').addEventListener('click', downloadMilitaryData); + document.getElementById('militaryWiki').addEventListener('click', () => wiki('Military-Forces')); - body.addEventListener("change", function(ev) { - const el = ev.target, line = el.parentNode, state = +line.dataset.id; + body.addEventListener('change', function (ev) { + const el = ev.target, + line = el.parentNode, + state = +line.dataset.id; changeAlert(state, line, +el.value); }); - body.addEventListener("click", function(ev) { - const el = ev.target, line = el.parentNode, state = +line.dataset.id; - if (el.tagName === "SPAN") overviewRegiments(state); + body.addEventListener('click', function (ev) { + const el = ev.target, + line = el.parentNode, + state = +line.dataset.id; + if (el.tagName === 'SPAN') overviewRegiments(state); }); // update military types in header and tooltips function updateHeaders() { - const header = document.getElementById("militaryHeader"); - header.querySelectorAll(".removable").forEach(el => el.remove()); - const insert = html => document.getElementById("militaryTotal").insertAdjacentHTML("beforebegin", html); + const header = document.getElementById('militaryHeader'); + header.querySelectorAll('.removable').forEach((el) => el.remove()); + const insert = (html) => document.getElementById('militaryTotal').insertAdjacentHTML('beforebegin', html); for (const u of options.military) { const label = capitalize(u.name.replace(/_/g, ' ')); insert(`
${label} 
`); } - header.querySelectorAll(".removable").forEach(function(e) { - e.addEventListener("click", function() {sortLines(this);}); + header.querySelectorAll('.removable').forEach(function (e) { + e.addEventListener('click', function () { + sortLines(this); + }); }); } // add line for each state function addLines() { - body.innerHTML = ""; - let lines = ""; - const states = pack.states.filter(s => s.i && !s.removed); + body.innerHTML = ''; + let lines = ''; + const states = pack.states.filter((s) => s.i && !s.removed); for (const s of states) { const population = rn((s.rural + s.urban * urbanization.value) * populationRate.value); - const getForces = u => s.military.reduce((s, r) => s+(r.u[u.name]||0), 0); + const getForces = (u) => s.military.reduce((s, r) => s + (r.u[u.name] || 0), 0); const total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0); - const rate = total / population * 100; + const rate = (total / population) * 100; - const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" "); - const lineData = options.military.map(u => `
${getForces(u)}
`).join(" "); + const sortData = options.military.map((u) => `data-${u.name}="${getForces(u)}"`).join(' '); + const lineData = options.military.map((u) => `
${getForces(u)}
`).join(' '); lines += `
@@ -78,14 +86,17 @@ function overviewMilitary() {
`; } - body.insertAdjacentHTML("beforeend", lines); + body.insertAdjacentHTML('beforeend', lines); updateFooter(); // add listeners - body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev))); - body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev))); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('mouseenter', (ev) => stateHighlightOn(ev))); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('mouseleave', (ev) => stateHighlightOff(ev))); - if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();} + if (body.dataset.type === 'percentage') { + body.dataset.type = 'absolute'; + togglePercentageMode(); + } applySorting(militaryHeader); } @@ -94,191 +105,206 @@ function overviewMilitary() { const dif = s.alert || alert ? alert / s.alert : 0; // modifier s.alert = line.dataset.alert = alert; - s.military.forEach(r => { - Object.keys(r.u).forEach(u => r.u[u] = rn(r.u[u] * dif)); // change units value + s.military.forEach((r) => { + Object.keys(r.u).forEach((u) => (r.u[u] = rn(r.u[u] * dif))); // change units value r.a = d3.sum(Object.values(r.u)); // change total armies.select(`g>g#regiment${s.i}-${r.i}>text`).text(Military.getTotal(r)); // change icon text }); - const getForces = u => s.military.reduce((s, r) => s+(r.u[u.name]||0), 0); - options.military.forEach(u => line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u)); + const getForces = (u) => s.military.reduce((s, r) => s + (r.u[u.name] || 0), 0); + options.military.forEach((u) => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u))); const population = rn((s.rural + s.urban * urbanization.value) * populationRate.value); - const total = line.dataset.total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0); - const rate = line.dataset.rate = total / population * 100; + const total = (line.dataset.total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0)); + const rate = (line.dataset.rate = (total / population) * 100); line.querySelector("div[data-type='total']").innerHTML = si(total); - line.querySelector("div[data-type='rate']").innerHTML = rn(rate, 2) + "%"; + line.querySelector("div[data-type='rate']").innerHTML = rn(rate, 2) + '%'; updateFooter(); } function updateFooter() { - const lines = Array.from(body.querySelectorAll(":scope > div")); - const statesNumber = militaryFooterStates.innerHTML = pack.states.filter(s => s.i && !s.removed).length; - const total = d3.sum(lines.map(el => el.dataset.total)); + const lines = Array.from(body.querySelectorAll(':scope > div')); + const statesNumber = (militaryFooterStates.innerHTML = pack.states.filter((s) => s.i && !s.removed).length); + const total = d3.sum(lines.map((el) => el.dataset.total)); militaryFooterForcesTotal.innerHTML = si(total); militaryFooterForces.innerHTML = si(total / statesNumber); - militaryFooterRate.innerHTML = rn(d3.sum(lines.map(el => el.dataset.rate)) / statesNumber, 2) + "%"; - militaryFooterAlert.innerHTML = rn(d3.sum(lines.map(el => el.dataset.alert)) / statesNumber, 2); + militaryFooterRate.innerHTML = rn(d3.sum(lines.map((el) => el.dataset.rate)) / statesNumber, 2) + '%'; + militaryFooterAlert.innerHTML = rn(d3.sum(lines.map((el) => el.dataset.alert)) / statesNumber, 2); } function stateHighlightOn(event) { const state = +event.target.dataset.id; if (customization || !state) return; - armies.select("#army"+state).transition().duration(2000).style("fill", "#ff0000"); + armies + .select('#army' + state) + .transition() + .duration(2000) + .style('fill', '#ff0000'); - if (!layerIsOn("toggleStates")) return; - const d = regions.select("#state"+state).attr("d"); + if (!layerIsOn('toggleStates')) return; + const d = regions.select('#state' + state).attr('d'); - const path = debug.append("path").attr("class", "highlight").attr("d", d) - .attr("fill", "none").attr("stroke", "red").attr("stroke-width", 1).attr("opacity", 1) - .attr("filter", "url(#blur1)"); + const path = debug.append('path').attr('class', 'highlight').attr('d', d).attr('fill', 'none').attr('stroke', 'red').attr('stroke-width', 1).attr('opacity', 1).attr('filter', 'url(#blur1)'); - const l = path.node().getTotalLength(), dur = (l + 5000) / 2; - const i = d3.interpolateString("0," + l, l + "," + l); - path.transition().duration(dur).attrTween("stroke-dasharray", function() {return t => i(t)}); + const l = path.node().getTotalLength(), + dur = (l + 5000) / 2; + const i = d3.interpolateString('0,' + l, l + ',' + l); + path + .transition() + .duration(dur) + .attrTween('stroke-dasharray', function () { + return (t) => i(t); + }); } function stateHighlightOff(event) { - debug.selectAll(".highlight").each(function() { - d3.select(this).transition().duration(1000).attr("opacity", 0).remove(); + debug.selectAll('.highlight').each(function () { + d3.select(this).transition().duration(1000).attr('opacity', 0).remove(); }); const state = +event.target.dataset.id; - armies.select("#army"+state).transition().duration(1000).style("fill", null); + armies + .select('#army' + state) + .transition() + .duration(1000) + .style('fill', null); } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; - const lines = body.querySelectorAll(":scope > div"); - const array = Array.from(lines), cache = []; + if (body.dataset.type === 'absolute') { + body.dataset.type = 'percentage'; + const lines = body.querySelectorAll(':scope > div'); + const array = Array.from(lines), + cache = []; - const total = function(type) { + const total = function (type) { if (cache[type]) cache[type]; - cache[type] = d3.sum(array.map(el => +el.dataset[type])); + cache[type] = d3.sum(array.map((el) => +el.dataset[type])); return cache[type]; - } + }; - lines.forEach(function(el) { - el.querySelectorAll("div").forEach(function(div) { + lines.forEach(function (el) { + el.querySelectorAll('div').forEach(function (div) { const type = div.dataset.type; - if (type === "rate") return; - div.textContent = total(type) ? rn(+el.dataset[type] / total(type) * 100) + "%" : "0%"; + if (type === 'rate') return; + div.textContent = total(type) ? rn((+el.dataset[type] / total(type)) * 100) + '%' : '0%'; }); }); } else { - body.dataset.type = "absolute"; + body.dataset.type = 'absolute'; addLines(); } } function militaryCustomize() { - const types = ["melee", "ranged", "mounted", "machinery", "naval", "armored", "aviation", "magical"]; - const table = document.getElementById("militaryOptions").querySelector("tbody"); + const types = ['melee', 'ranged', 'mounted', 'machinery', 'naval', 'armored', 'aviation', 'magical']; + const table = document.getElementById('militaryOptions').querySelector('tbody'); removeUnitLines(); - options.military.map(u => addUnitLine(u)); + options.military.map((u) => addUnitLine(u)); - $("#militaryOptions").dialog({ - title: "Edit Military Units", resizable: false, width: fitContent(), - position: {my: "center", at: "center", of: "svg"}, + $('#militaryOptions').dialog({ + title: 'Edit Military Units', + resizable: false, + width: fitContent(), + position: {my: 'center', at: 'center', of: 'svg'}, buttons: { Apply: applyMilitaryOptions, - Add: () => addUnitLine({icon: "🛡️", name: "custom"+militaryOptionsTable.rows.length, rural: .2, urban: .5, crew: 1, power: 1, type: "melee"}), + Add: () => addUnitLine({icon: '🛡️', name: 'custom' + militaryOptionsTable.rows.length, rural: 0.2, urban: 0.5, crew: 1, power: 1, type: 'melee'}), Restore: restoreDefaultUnits, - Cancel: function() {$(this).dialog("close");} - }, open: function() { - const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button"); - buttons[0].addEventListener("mousemove", () => tip("Apply military units settings. All forces will be recalculated!")); - buttons[1].addEventListener("mousemove", () => tip("Add new military unit to the table")); - buttons[2].addEventListener("mousemove", () => tip("Restore default military units and settings")); - buttons[3].addEventListener("mousemove", () => tip("Close the window without saving the changes")); + Cancel: function () { + $(this).dialog('close'); + } + }, + open: function () { + const buttons = $(this).dialog('widget').find('.ui-dialog-buttonset > button'); + buttons[0].addEventListener('mousemove', () => tip("Apply military units settings. All forces will be recalculated!")); + buttons[1].addEventListener('mousemove', () => tip('Add new military unit to the table')); + buttons[2].addEventListener('mousemove', () => tip('Restore default military units and settings')); + buttons[3].addEventListener('mousemove', () => tip('Close the window without saving the changes')); } }); function removeUnitLines() { - table.querySelectorAll("tr").forEach(el => el.remove()); + table.querySelectorAll('tr').forEach((el) => el.remove()); } function addUnitLine(u) { - const row = document.createElement("tr"); - row.innerHTML = ` + const row = document.createElement('tr'); + row.innerHTML = ` - + - + `; - row.querySelector("button").addEventListener("click", function(e) {selectIcon(this.innerHTML, v => this.innerHTML = v)}); + row.querySelector('button').addEventListener('click', function (e) { + selectIcon(this.innerHTML, (v) => (this.innerHTML = v)); + }); table.appendChild(row); } function restoreDefaultUnits() { removeUnitLines(); - Military.getDefaultOptions().map(u => addUnitLine(u)); + Military.getDefaultOptions().map((u) => addUnitLine(u)); } function applyMilitaryOptions() { - const unitLines = Array.from(table.querySelectorAll("tr")); - const names = unitLines.map(r => r.querySelector("input").value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, '_')); + const unitLines = Array.from(table.querySelectorAll('tr')); + const names = unitLines.map((r) => r.querySelector('input').value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, '_')); if (new Set(names).size !== names.length) { - tip("All units should have unique names", false, "error"); + tip('All units should have unique names', false, 'error'); return; } - $("#militaryOptions").dialog("close"); + $('#militaryOptions').dialog('close'); options.military = unitLines.map((r, i) => { - const [icon, name, rural, urban, crew, power, type, separate] = Array.from(r.querySelectorAll("input, select, button")).map(d => { + const [icon, name, rural, urban, crew, power, type, separate] = Array.from(r.querySelectorAll('input, select, button')).map((d) => { let value = d.value; - if (d.type === "number") value = +d.value || 0; - if (d.type === "checkbox") value = +d.checked || 0; - if (d.type === "button") value = d.innerHTML || "⠀"; + if (d.type === 'number') value = +d.value || 0; + if (d.type === 'checkbox') value = +d.checked || 0; + if (d.type === 'button') value = d.innerHTML || '⠀'; return value; }); - return {icon, name:names[i], rural, urban, crew, power, type, separate}; + return {icon, name: names[i], rural, urban, crew, power, type, separate}; }); - localStorage.setItem("military", JSON.stringify(options.military)); + localStorage.setItem('military', JSON.stringify(options.military)); Military.generate(); updateHeaders(); addLines(); } - } function militaryRecalculate() { - alertMessage.innerHTML = "Are you sure you want to recalculate military forces for all states?
Regiments for all states will be regenerated"; - $("#alert").dialog({resizable: false, title: "Remove regiment", - buttons: { - Recalculate: function() { - $(this).dialog("close"); - Military.generate(); - addLines(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to recalculate military forces for all states?
Regiments for all states will be regenerated'; + const onConfirm = () => { + Military.generate(); + addLines(); + }; + confirmationDialog({title: 'Remove regiment', message, confirm: 'Remove', onConfirm}); } function downloadMilitaryData() { - const units = options.military.map(u => u.name); - let data = "Id,State,"+units.map(u => capitalize(u)).join(",")+",Total,Population,Rate,War Alert\n"; // headers + const units = options.military.map((u) => u.name); + let data = 'Id,State,' + units.map((u) => capitalize(u)).join(',') + ',Total,Population,Rate,War Alert\n'; // headers - body.querySelectorAll(":scope > div").forEach(function(el) { - data += el.dataset.id + ","; - data += el.dataset.state + ","; - data += units.map(u => el.dataset[u]).join(",") + ","; - data += el.dataset.total + ","; - data += el.dataset.population + ","; - data += rn(el.dataset.rate,2) + "%,"; - data += el.dataset.alert + "\n"; + body.querySelectorAll(':scope > div').forEach(function (el) { + data += el.dataset.id + ','; + data += el.dataset.state + ','; + data += units.map((u) => el.dataset[u]).join(',') + ','; + data += el.dataset.total + ','; + data += el.dataset.population + ','; + data += rn(el.dataset.rate, 2) + '%,'; + data += el.dataset.alert + '\n'; }); - const name = getFileName("Military") + ".csv"; + const name = getFileName('Military') + '.csv'; downloadFile(data, name); } - -} \ No newline at end of file +} diff --git a/modules/ui/namesbase-editor.js b/modules/ui/namesbase-editor.js index 03149a13..76d08704 100644 --- a/modules/ui/namesbase-editor.js +++ b/modules/ui/namesbase-editor.js @@ -1,74 +1,80 @@ -"use strict"; +'use strict'; function editNamesbase() { if (customization) return; - closeDialogs("#namesbaseEditor, .stable"); - $("#namesbaseEditor").dialog(); + closeDialogs('#namesbaseEditor, .stable'); + $('#namesbaseEditor').dialog(); if (modules.editNamesbase) return; modules.editNamesbase = true; // add listeners - document.getElementById("namesbaseSelect").addEventListener("change", updateInputs); - document.getElementById("namesbaseTextarea").addEventListener("change", updateNamesData); - document.getElementById("namesbaseUpdateExamples").addEventListener("click", updateExamples); - document.getElementById("namesbaseExamples").addEventListener("click", updateExamples); - document.getElementById("namesbaseName").addEventListener("input", updateBaseName); - document.getElementById("namesbaseMin").addEventListener("input", updateBaseMin); - document.getElementById("namesbaseMax").addEventListener("input", updateBaseMax); - document.getElementById("namesbaseDouble").addEventListener("input", updateBaseDublication); - document.getElementById("namesbaseAdd").addEventListener("click", namesbaseAdd); - document.getElementById("namesbaseAnalize").addEventListener("click", analizeNamesbase); - document.getElementById("namesbaseDefault").addEventListener("click", namesbaseRestoreDefault); - document.getElementById("namesbaseDownload").addEventListener("click", namesbaseDownload); - document.getElementById("namesbaseUpload").addEventListener("click", () => namesbaseToLoad.click()); - document.getElementById("namesbaseToLoad").addEventListener("change", function() {uploadFile(this, namesbaseUpload)}); - document.getElementById("namesbaseSpeak").addEventListener("click", () => speak(namesbaseExamples.textContent)); + document.getElementById('namesbaseSelect').addEventListener('change', updateInputs); + document.getElementById('namesbaseTextarea').addEventListener('change', updateNamesData); + document.getElementById('namesbaseUpdateExamples').addEventListener('click', updateExamples); + document.getElementById('namesbaseExamples').addEventListener('click', updateExamples); + document.getElementById('namesbaseName').addEventListener('input', updateBaseName); + document.getElementById('namesbaseMin').addEventListener('input', updateBaseMin); + document.getElementById('namesbaseMax').addEventListener('input', updateBaseMax); + document.getElementById('namesbaseDouble').addEventListener('input', updateBaseDublication); + document.getElementById('namesbaseAdd').addEventListener('click', namesbaseAdd); + document.getElementById('namesbaseAnalize').addEventListener('click', analizeNamesbase); + document.getElementById('namesbaseDefault').addEventListener('click', namesbaseRestoreDefault); + document.getElementById('namesbaseDownload').addEventListener('click', namesbaseDownload); + document.getElementById('namesbaseUpload').addEventListener('click', () => namesbaseToLoad.click()); + document.getElementById('namesbaseToLoad').addEventListener('change', function () { + uploadFile(this, namesbaseUpload); + }); + document.getElementById('namesbaseSpeak').addEventListener('click', () => speak(namesbaseExamples.textContent)); createBasesList(); updateInputs(); - $("#namesbaseEditor").dialog({ - title: "Namesbase Editor", width: "42.5em", - position: {my: "center", at: "center", of: "svg"} + $('#namesbaseEditor').dialog({ + title: 'Namesbase Editor', + width: '42.5em', + position: {my: 'center', at: 'center', of: 'svg'} }); function createBasesList() { - const select = document.getElementById("namesbaseSelect"); - select.innerHTML = ""; + const select = document.getElementById('namesbaseSelect'); + select.innerHTML = ''; nameBases.forEach((b, i) => select.options.add(new Option(b.name, i))); } function updateInputs() { - const base = +document.getElementById("namesbaseSelect").value; - if (!nameBases[base]) {tip(`Namesbase ${base} is not defined`, false, "error"); return;} - document.getElementById("namesbaseTextarea").value = nameBases[base].b; - document.getElementById("namesbaseName").value = nameBases[base].name; - document.getElementById("namesbaseMin").value = nameBases[base].min; - document.getElementById("namesbaseMax").value = nameBases[base].max; - document.getElementById("namesbaseDouble").value = nameBases[base].d; + const base = +document.getElementById('namesbaseSelect').value; + if (!nameBases[base]) { + tip(`Namesbase ${base} is not defined`, false, 'error'); + return; + } + document.getElementById('namesbaseTextarea').value = nameBases[base].b; + document.getElementById('namesbaseName').value = nameBases[base].name; + document.getElementById('namesbaseMin').value = nameBases[base].min; + document.getElementById('namesbaseMax').value = nameBases[base].max; + document.getElementById('namesbaseDouble').value = nameBases[base].d; updateExamples(); } function updateExamples() { - const base = +document.getElementById("namesbaseSelect").value; - let examples = ""; - for (let i=0; i < 10; i++) { + const base = +document.getElementById('namesbaseSelect').value; + let examples = ''; + for (let i = 0; i < 10; i++) { const example = Names.getBase(base); if (example === undefined) { - examples = "Cannot generate examples. Please verify the data"; + examples = 'Cannot generate examples. Please verify the data'; break; } - if (i) examples += ", "; + if (i) examples += ', '; examples += example; } - document.getElementById("namesbaseExamples").innerHTML = examples; + document.getElementById('namesbaseExamples').innerHTML = examples; } function updateNamesData() { - const base = +document.getElementById("namesbaseSelect").value; - const b = document.getElementById("namesbaseTextarea").value; - if (b.split(",").length < 3) { - tip("The names data provided is too short of incorrect", false, "error"); + const base = +document.getElementById('namesbaseSelect').value; + const b = document.getElementById('namesbaseTextarea').value; + if (b.split(',').length < 3) { + tip('The names data provided is too short of incorrect', false, 'error'); return; } nameBases[base].b = b; @@ -76,68 +82,91 @@ function editNamesbase() { } function updateBaseName() { - const base = +document.getElementById("namesbaseSelect").value; - const select = document.getElementById("namesbaseSelect"); + const base = +document.getElementById('namesbaseSelect').value; + const select = document.getElementById('namesbaseSelect'); select.options[namesbaseSelect.selectedIndex].innerHTML = this.value; nameBases[base].name = this.value; } function updateBaseMin() { - const base = +document.getElementById("namesbaseSelect").value; - if (+this.value > nameBases[base].max) {tip("Minimal length cannot be greater than maximal", false, "error"); return;} + const base = +document.getElementById('namesbaseSelect').value; + if (+this.value > nameBases[base].max) { + tip('Minimal length cannot be greater than maximal', false, 'error'); + return; + } nameBases[base].min = +this.value; } function updateBaseMax() { - const base = +document.getElementById("namesbaseSelect").value; - if (+this.value < nameBases[base].min) {tip("Maximal length should be greater than minimal", false, "error"); return;} + const base = +document.getElementById('namesbaseSelect').value; + if (+this.value < nameBases[base].min) { + tip('Maximal length should be greater than minimal', false, 'error'); + return; + } nameBases[base].max = +this.value; } function updateBaseDublication() { - const base = +document.getElementById("namesbaseSelect").value; + const base = +document.getElementById('namesbaseSelect').value; nameBases[base].d = this.value; } function analizeNamesbase() { - const string = document.getElementById("namesbaseTextarea").value; - if (!string) {tip("Names data field should not be empty", false, "error"); return;} + const string = document.getElementById('namesbaseTextarea').value; + if (!string) { + tip('Names data field should not be empty', false, 'error'); + return; + } const base = string.toLowerCase(); - const array = base.split(","); + const array = base.split(','); const l = array.length; - if (!l) {tip("Names data should not be empty", false, "error"); return;} + if (!l) { + tip('Names data should not be empty', false, 'error'); + return; + } - const wordsLength = array.map(n => n.length); - const multi = rn(d3.mean(array.map(n => (n.match(/ /i)||[]).length)) * 100, 2); - const geminate = array.map(name => name.match(/[^\w\s]|(.)(?=\1)/g)||[]).flat(); - const doubled = ([...new Set(geminate)].filter(l => geminate.filter(d => d === l).length > 3)||["none"]).join(""); + const wordsLength = array.map((n) => n.length); + const multi = rn(d3.mean(array.map((n) => (n.match(/ /i) || []).length)) * 100, 2); + const geminate = array.map((name) => name.match(/[^\w\s]|(.)(?=\1)/g) || []).flat(); + const doubled = ([...new Set(geminate)].filter((l) => geminate.filter((d) => d === l).length > 3) || ['none']).join(''); const chain = Names.calculateChain(string); - const depth = rn(d3.mean(Object.keys(chain).map(key => chain[key].filter(c => c !== " ").length))); - const nonLatin = (string.match(/[^\u0000-\u007f]/g)||["none"]).join(""); + const depth = rn(d3.mean(Object.keys(chain).map((key) => chain[key].filter((c) => c !== ' ').length))); + const nonLatin = (string.match(/[^\u0000-\u007f]/g) || ['none']).join(''); - const lengthStat = - l < 30 ? "[not enough]" : - l < 150 ? "[low]" : - l < 150 ? "[low]" : - l < 400 ? "[good]" : - l < 600 ? "[overmuch]" : - "[overmuch]"; + const lengthStat = + l < 30 + ? "[not enough]" + : l < 150 + ? "[low]" + : l < 150 + ? "[low]" + : l < 400 + ? "[good]" + : l < 600 + ? "[overmuch]" + : "[overmuch]"; - const rangeStat = - l < 10 ? "[low]" : - l < 15 ? "[low]" : - l < 20 ? "[low]" : - "[good]"; + const rangeStat = + l < 10 + ? "[low]" + : l < 15 + ? "[low]" + : l < 20 + ? "[low]" + : "[good]"; - const depthStat = - l < 15 ? "[low]" : - l < 20 ? "[low]" : - l < 25 ? "[low]" : - "[good]"; + const depthStat = + l < 15 + ? "[low]" + : l < 20 + ? "[low]" + : l < 25 + ? "[low]" + : "[good]"; alertMessage.innerHTML = `
Namesbase length: ${l} ${lengthStat}
-
Namesbase range: ${Object.keys(chain).length-1} ${rangeStat}
+
Namesbase range: ${Object.keys(chain).length - 1} ${rangeStat}
Namesbase depth: ${depth} ${depthStat}
Non-basic chars: ${nonLatin}

@@ -148,58 +177,61 @@ function editNamesbase() {
Doubled chars: ${doubled}
Multi-word names: ${multi}%
`; - $("#alert").dialog({ - resizable: false, title: "Data Analysis", - position: {my: "left top-30", at: "right+10 top", of: "#namesbaseEditor"}, - buttons: {OK: function() {$(this).dialog("close");}} + $('#alert').dialog({ + resizable: false, + title: 'Data Analysis', + position: {my: 'left top-30', at: 'right+10 top', of: '#namesbaseEditor'}, + buttons: { + OK: function () { + $(this).dialog('close'); + } + } }); } function namesbaseAdd() { const base = nameBases.length; - const b = "This,is,an,example,of,name,base,showing,correct,format,It,should,have,at,least,one,hundred,names,separated,with,comma"; - nameBases.push({name: "Base" + base, min: 5, max: 12, d: "", m: 0, b}); - document.getElementById("namesbaseSelect").add(new Option("Base" + base, base)); - document.getElementById("namesbaseSelect").value = base; - document.getElementById("namesbaseTextarea").value = b; - document.getElementById("namesbaseName").value = "Base" + base; - document.getElementById("namesbaseMin").value = 5; - document.getElementById("namesbaseMax").value = 12; - document.getElementById("namesbaseDouble").value = ""; - document.getElementById("namesbaseExamples").innerHTML = "Please provide names data"; + const b = 'This,is,an,example,of,name,base,showing,correct,format,It,should,have,at,least,one,hundred,names,separated,with,comma'; + nameBases.push({name: 'Base' + base, min: 5, max: 12, d: '', m: 0, b}); + document.getElementById('namesbaseSelect').add(new Option('Base' + base, base)); + document.getElementById('namesbaseSelect').value = base; + document.getElementById('namesbaseTextarea').value = b; + document.getElementById('namesbaseName').value = 'Base' + base; + document.getElementById('namesbaseMin').value = 5; + document.getElementById('namesbaseMax').value = 12; + document.getElementById('namesbaseDouble').value = ''; + document.getElementById('namesbaseExamples').innerHTML = 'Please provide names data'; } function namesbaseRestoreDefault() { - alertMessage.innerHTML = `Are you sure you want to restore default namesbase?`; - $("#alert").dialog({resizable: false, title: "Restore default data", - buttons: { - Restore: function() { - $(this).dialog("close"); - Names.clearChains(); - nameBases = Names.getNameBases(); - createBasesList(); - updateInputs(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to restore default namesbase?
This action cannot be reverted'; + const onConfirm = () => { + Names.clearChains(); + nameBases = Names.getNameBases(); + createBasesList(); + updateInputs(); + }; + confirmationDialog({title: 'Restore default data', message, confirm: 'Restore', onConfirm}); } function namesbaseDownload() { - const data = nameBases.map((b,i) => `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${b.b}`).join("\r\n"); - const name = getFileName("Namesbase") + ".txt"; + const data = nameBases.map((b, i) => `${b.name}|${b.min}|${b.max}|${b.d}|${b.m}|${b.b}`).join('\r\n'); + const name = getFileName('Namesbase') + '.txt'; downloadFile(data, name); } function namesbaseUpload(dataLoaded) { - const data = dataLoaded.split("\r\n"); - if (!data || !data[0]) {tip("Cannot load a namesbase. Please check the data format", false, "error"); return;} + const data = dataLoaded.split('\r\n'); + if (!data || !data[0]) { + tip('Cannot load a namesbase. Please check the data format', false, 'error'); + return; + } Names.clearChains(); nameBases = []; - data.forEach(d => { - const e = d.split("|"); - nameBases.push({name:e[0], min:e[1], max:e[2], d:e[3], m:e[4], b:e[5]}); + data.forEach((d) => { + const e = d.split('|'); + nameBases.push({name: e[0], min: e[1], max: e[2], d: e[3], m: e[4], b: e[5]}); }); createBasesList(); diff --git a/modules/ui/notes-editor.js b/modules/ui/notes-editor.js index 04facf08..624673ce 100644 --- a/modules/ui/notes-editor.js +++ b/modules/ui/notes-editor.js @@ -1,16 +1,18 @@ -"use strict"; +'use strict'; function editNotes(id, name) { // update list of objects - const select = document.getElementById("notesSelect"); + const select = document.getElementById('notesSelect'); select.options.length = 0; - for (const note of notes) {select.options.add(new Option(note.id, note.id));} + for (const note of notes) { + select.options.add(new Option(note.id, note.id)); + } // initiate pell (html editor) const editor = Pell.init({ - element: document.getElementById("notesText"), - onChange: html => { - const id = document.getElementById("notesSelect").value; - const note = notes.find(note => note.id === id); + element: document.getElementById('notesText'), + onChange: (html) => { + const id = document.getElementById('notesSelect').value; + const note = notes.find((note) => note.id === id); if (!note) return; note.legend = html; showNote(note); @@ -20,10 +22,10 @@ function editNotes(id, name) { // select an object if (notes.length || id) { if (!id) id = notes[0].id; - let note = notes.find(note => note.id === id); + let note = notes.find((note) => note.id === id); if (note === undefined) { if (!name) name = id; - note = {id, name, legend: ""}; + note = {id, name, legend: ''}; notes.push(note); select.options.add(new Option(id, id)); } @@ -32,102 +34,94 @@ function editNotes(id, name) { editor.content.innerHTML = note.legend; showNote(note); } else { - editor.content.innerHTML = "There are no added notes. Click on element (e.g. label) and add a free text note"; - document.getElementById("notesName").value = ""; + editor.content.innerHTML = 'There are no added notes. Click on element (e.g. label) and add a free text note'; + document.getElementById('notesName').value = ''; } // open a dialog - $("#notesEditor").dialog({ - title: "Notes Editor", minWidth: "40em", width: "50vw", - position: {my: "center", at: "center", of: "svg"}, - close: () => notesText.innerHTML = "" + $('#notesEditor').dialog({ + title: 'Notes Editor', + minWidth: '40em', + width: '50vw', + position: {my: 'center', at: 'center', of: 'svg'}, + close: () => (notesText.innerHTML = '') }); if (modules.editNotes) return; modules.editNotes = true; // add listeners - document.getElementById("notesSelect").addEventListener("change", changeObject); - document.getElementById("notesName").addEventListener("input", changeName); - document.getElementById("notesPin").addEventListener("click", () => options.pinNotes = !options.pinNotes); - document.getElementById("notesSpeak").addEventListener("click", () => speak(editor.content.innerHTML)); - document.getElementById("notesFocus").addEventListener("click", validateHighlightElement); - document.getElementById("notesDownload").addEventListener("click", downloadLegends); - document.getElementById("notesUpload").addEventListener("click", () => legendsToLoad.click()); - document.getElementById("legendsToLoad").addEventListener("change", function() {uploadFile(this, uploadLegends)}); - document.getElementById("notesRemove").addEventListener("click", triggerNotesRemove); + document.getElementById('notesSelect').addEventListener('change', changeObject); + document.getElementById('notesName').addEventListener('input', changeName); + document.getElementById('notesPin').addEventListener('click', () => (options.pinNotes = !options.pinNotes)); + document.getElementById('notesSpeak').addEventListener('click', () => speak(editor.content.innerHTML)); + document.getElementById('notesFocus').addEventListener('click', validateHighlightElement); + document.getElementById('notesDownload').addEventListener('click', downloadLegends); + document.getElementById('notesUpload').addEventListener('click', () => legendsToLoad.click()); + document.getElementById('legendsToLoad').addEventListener('change', function () { + uploadFile(this, uploadLegends); + }); + document.getElementById('notesRemove').addEventListener('click', triggerNotesRemove); function showNote(note) { - document.getElementById("notes").style.display = "block"; - document.getElementById("notesHeader").innerHTML = note.name; - document.getElementById("notesBody").innerHTML = note.legend; + document.getElementById('notes').style.display = 'block'; + document.getElementById('notesHeader').innerHTML = note.name; + document.getElementById('notesBody').innerHTML = note.legend; } function changeObject() { - const note = notes.find(note => note.id === this.value); + const note = notes.find((note) => note.id === this.value); if (!note) return; notesName.value = note.name; editor.content.innerHTML = note.legend; } function changeName() { - const id = document.getElementById("notesSelect").value; - const note = notes.find(note => note.id === id); + const id = document.getElementById('notesSelect').value; + const note = notes.find((note) => note.id === id); if (!note) return; note.name = this.value; showNote(note); } function validateHighlightElement() { - const select = document.getElementById("notesSelect"); + const select = document.getElementById('notesSelect'); const element = document.getElementById(select.value); - // if element is not found if (element === null) { - alertMessage.innerHTML = "Related element is not found. Would you like to remove the note?"; - $("#alert").dialog({resizable: false, title: "Element not found", - buttons: { - Remove: function() {$(this).dialog("close"); removeLegend();}, - Keep: function() {$(this).dialog("close");} - } - }); + const message = 'Related element is not found. Would you like to remove the note?'; + confirmationDialog({title: 'Element not found', message, confirm: 'Remove', onConfirm: removeLegend}); return; } - + highlightElement(element); // if element is found } function downloadLegends() { const data = JSON.stringify(notes); - const name = getFileName("Notes") + ".txt"; + const name = getFileName('Notes') + '.txt'; downloadFile(data, name); } function uploadLegends(dataLoaded) { - if (!dataLoaded) {tip("Cannot load the file. Please check the data format", false, "error"); return;} + if (!dataLoaded) return tip('Cannot load the file. Please check the data format', false, 'error'); notes = JSON.parse(dataLoaded); - document.getElementById("notesSelect").options.length = 0; + document.getElementById('notesSelect').options.length = 0; editNotes(notes[0].id, notes[0].name); } function triggerNotesRemove() { - alertMessage.innerHTML = "Are you sure you want to remove the selected note?"; - $("#alert").dialog({resizable: false, title: "Remove note", - buttons: { - Remove: function() {$(this).dialog("close"); removeLegend();}, - Keep: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the selected note?
This action cannot be reverted'; + confirmationDialog({title: 'Remove note', message, confirm: 'Remove', onConfirm: removeLegend}); } function removeLegend() { - const select = document.getElementById("notesSelect"); - const index = notes.findIndex(n => n.id === select.value); + const select = document.getElementById('notesSelect'); + const index = notes.findIndex((n) => n.id === select.value); notes.splice(index, 1); select.options.length = 0; - if (!notes.length) {$("#notesEditor").dialog("close"); return;} - notesText.innerHTML = ""; + if (!notes.length) return $('#notesEditor').dialog('close'); + notesText.innerHTML = ''; editNotes(notes[0].id, notes[0].name); } - -} \ No newline at end of file +} diff --git a/modules/ui/options.js b/modules/ui/options.js index 888d22fb..419914d4 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -1,73 +1,77 @@ // UI module to control the options (preferences) -"use strict"; +'use strict'; -$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"}); -$("#exitCustomization").draggable({handle: "div"}); -$("#mapLayers").disableSelection(); +$('#optionsContainer').draggable({handle: '.drag-trigger', snap: 'svg', snapMode: 'both'}); +$('#exitCustomization').draggable({handle: 'div'}); +$('#mapLayers').disableSelection(); // remove glow if tip is aknowledged -if (localStorage.getItem("disable_click_arrow_tooltip")) { +if (localStorage.getItem('disable_click_arrow_tooltip')) { clearMainTip(); - optionsTrigger.classList.remove("glow"); + optionsTrigger.classList.remove('glow'); } // Show options pane on trigger click function showOptions(event) { - if (!localStorage.getItem("disable_click_arrow_tooltip")) { + if (!localStorage.getItem('disable_click_arrow_tooltip')) { clearMainTip(); - localStorage.setItem("disable_click_arrow_tooltip", true); - optionsTrigger.classList.remove("glow"); + localStorage.setItem('disable_click_arrow_tooltip', true); + optionsTrigger.classList.remove('glow'); } - regenerate.style.display = "none"; - document.getElementById("options").style.display = "block"; - optionsTrigger.style.display = "none"; + regenerate.style.display = 'none'; + document.getElementById('options').style.display = 'block'; + optionsTrigger.style.display = 'none'; if (event) event.stopPropagation(); } // Hide options pane on trigger click function hideOptions(event) { - document.getElementById("options").style.display = "none"; - optionsTrigger.style.display = "block"; + document.getElementById('options').style.display = 'none'; + optionsTrigger.style.display = 'block'; if (event) event.stopPropagation(); } // To toggle options on hotkey press function toggleOptions(event) { - if (document.getElementById("options").style.display === "none") showOptions(event); + if (document.getElementById('options').style.display === 'none') showOptions(event); else hideOptions(event); } // Toggle "New Map!" pane on hover -optionsTrigger.addEventListener("mouseenter", function() { - if (optionsTrigger.classList.contains("glow")) return; - if (document.getElementById("options").style.display === "none") regenerate.style.display = "block"; +optionsTrigger.addEventListener('mouseenter', function () { + if (optionsTrigger.classList.contains('glow')) return; + if (document.getElementById('options').style.display === 'none') regenerate.style.display = 'block'; }); -collapsible.addEventListener("mouseleave", function() { - regenerate.style.display = "none"; +collapsible.addEventListener('mouseleave', function () { + regenerate.style.display = 'none'; }); // Activate options tab on click -document.getElementById("options").querySelector("div.tab").addEventListener("click", function(event) { - if (event.target.tagName !== "BUTTON") return; - const id = event.target.id; - const active = document.getElementById("options").querySelector(".tab > button.active"); - if (active && id === active.id) return; // already active tab is clicked +document + .getElementById('options') + .querySelector('div.tab') + .addEventListener('click', function (event) { + if (event.target.tagName !== 'BUTTON') return; + const id = event.target.id; + const active = document.getElementById('options').querySelector('.tab > button.active'); + if (active && id === active.id) return; // already active tab is clicked - if (active) active.classList.remove("active"); - document.getElementById(id).classList.add("active"); - document.getElementById("options").querySelectorAll(".tabcontent").forEach(e => e.style.display = "none"); + if (active) active.classList.remove('active'); + document.getElementById(id).classList.add('active'); + document + .getElementById('options') + .querySelectorAll('.tabcontent') + .forEach((e) => (e.style.display = 'none')); - if (id === "layersTab") layersContent.style.display = "block"; else - if (id === "styleTab") styleContent.style.display = "block"; else - if (id === "optionsTab") optionsContent.style.display = "block"; else - if (id === "toolsTab") customization === 1 - ? customizationMenu.style.display = "block" - : toolsContent.style.display = "block"; else - if (id === "aboutTab") aboutContent.style.display = "block"; -}); + if (id === 'layersTab') layersContent.style.display = 'block'; + else if (id === 'styleTab') styleContent.style.display = 'block'; + else if (id === 'optionsTab') optionsContent.style.display = 'block'; + else if (id === 'toolsTab') customization === 1 ? (customizationMenu.style.display = 'block') : (toolsContent.style.display = 'block'); + else if (id === 'aboutTab') aboutContent.style.display = 'block'; + }); // show popup with a list of Patreon supportes (updated manually, to be replaced with API call) function showSupporters() { @@ -93,78 +97,87 @@ function showSupporters() { Justin Mcclain,Kristin Chernoff,Rowland Kingman,Esther Busch,Grayson McClead,Austin,Hakon the Viking,Chad Riley,Cooper Counts,Patrick Jones,Clonetone, PlayByMail.Net,Brad Wardell,Lance Saba,Egoensis,Brea Richards,Tiber,Chris Bloom,Maxim Lowe,Aquelion,Page One Project,Spencer Morris,Paul Ingram`; - const array = supporters.replace(/(?:\r\n|\r|\n)/g, "").split(",").map(v => capitalize(v.trim())).sort(); - alertMessage.innerHTML = ""; - $("#alert").dialog({resizable: false,title: "Patreon Supporters",width: "54vw",position: {my: "center",at: "center",of: "svg"}}); + const array = supporters + .replace(/(?:\r\n|\r|\n)/g, '') + .split(',') + .map((v) => capitalize(v.trim())) + .sort(); + alertMessage.innerHTML = "'; + $('#alert').dialog({resizable: false, title: 'Patreon Supporters', width: '54vw', position: {my: 'center', at: 'center', of: 'svg'}}); } // Option listeners -const optionsContent = document.getElementById("optionsContent"); -optionsContent.addEventListener("input", function(event) { - const id = event.target.id, value = event.target.value; - if (id === "mapWidthInput" || id === "mapHeightInput") mapSizeInputChange(); - else if (id === "pointsInput") changeCellsDensity(+value); - else if (id === "culturesInput") culturesOutput.value = value; - else if (id === "culturesOutput") culturesInput.value = value; - else if (id === "culturesSet") changeCultureSet(); - else if (id === "regionsInput" || id === "regionsOutput") changeStatesNumber(value); - else if (id === "provincesInput") provincesOutput.value = value; - else if (id === "provincesOutput") provincesOutput.value = value; - else if (id === "provincesOutput") powerOutput.value = value; - else if (id === "powerInput") powerOutput.value = value; - else if (id === "powerOutput") powerInput.value = value; - else if (id === "neutralInput") neutralOutput.value = value; - else if (id === "neutralOutput") neutralInput.value = value; - else if (id === "manorsInput") changeBurgsNumberSlider(value); - else if (id === "religionsInput") religionsOutput.value = value; - else if (id === "emblemShape") changeEmblemShape(value); - else if (id === "tooltipSizeInput" || id === "tooltipSizeOutput") changeTooltipSize(value); - else if (id === "transparencyInput") changeDialogsTransparency(value); +const optionsContent = document.getElementById('optionsContent'); +optionsContent.addEventListener('input', function (event) { + const id = event.target.id, + value = event.target.value; + if (id === 'mapWidthInput' || id === 'mapHeightInput') mapSizeInputChange(); + else if (id === 'pointsInput') changeCellsDensity(+value); + else if (id === 'culturesInput') culturesOutput.value = value; + else if (id === 'culturesOutput') culturesInput.value = value; + else if (id === 'culturesSet') changeCultureSet(); + else if (id === 'regionsInput' || id === 'regionsOutput') changeStatesNumber(value); + else if (id === 'provincesInput') provincesOutput.value = value; + else if (id === 'provincesOutput') provincesOutput.value = value; + else if (id === 'provincesOutput') powerOutput.value = value; + else if (id === 'powerInput') powerOutput.value = value; + else if (id === 'powerOutput') powerInput.value = value; + else if (id === 'neutralInput') neutralOutput.value = value; + else if (id === 'neutralOutput') neutralInput.value = value; + else if (id === 'manorsInput') changeBurgsNumberSlider(value); + else if (id === 'religionsInput') religionsOutput.value = value; + else if (id === 'emblemShape') changeEmblemShape(value); + else if (id === 'tooltipSizeInput' || id === 'tooltipSizeOutput') changeTooltipSize(value); + else if (id === 'transparencyInput') changeDialogsTransparency(value); }); -optionsContent.addEventListener("change", function(event) { +optionsContent.addEventListener('change', function (event) { if (event.target.dataset.stored) lock(event.target.dataset.stored); - const id = event.target.id, value = event.target.value; - if (id === "zoomExtentMin" || id === "zoomExtentMax") changeZoomExtent(value); - else if (id === "optionsSeed") generateMapWithSeed(); - else if (id === "uiSizeInput" || id === "uiSizeOutput") changeUIsize(value); - else if (id === "yearInput") changeYear(); - else if (id === "eraInput") changeEra(); + const id = event.target.id, + value = event.target.value; + if (id === 'zoomExtentMin' || id === 'zoomExtentMax') changeZoomExtent(value); + else if (id === 'optionsSeed') generateMapWithSeed(); + else if (id === 'uiSizeInput' || id === 'uiSizeOutput') changeUIsize(value); + else if (id === 'yearInput') changeYear(); + else if (id === 'eraInput') changeEra(); }); -optionsContent.addEventListener("click", function(event) { +optionsContent.addEventListener('click', function (event) { const id = event.target.id; - if (id === "toggleFullscreen") toggleFullscreen(); - else if (id === "optionsSeedGenerate") generateMapWithSeed(); - else if (id === "optionsMapHistory") showSeedHistoryDialog(); - else if (id === "optionsCopySeed") copyMapURL(); - else if (id === "optionsEraRegenerate") regenerateEra(); - else if (id === "zoomExtentDefault") restoreDefaultZoomExtent(); - else if (id === "translateExtent") toggleTranslateExtent(event.target); - else if (id === "speakerTest") testSpeaker(); + if (id === 'toggleFullscreen') toggleFullscreen(); + else if (id === 'optionsSeedGenerate') generateMapWithSeed(); + else if (id === 'optionsMapHistory') showSeedHistoryDialog(); + else if (id === 'optionsCopySeed') copyMapURL(); + else if (id === 'optionsEraRegenerate') regenerateEra(); + else if (id === 'zoomExtentDefault') restoreDefaultZoomExtent(); + else if (id === 'translateExtent') toggleTranslateExtent(event.target); + else if (id === 'speakerTest') testSpeaker(); }); function mapSizeInputChange() { changeMapSize(); - localStorage.setItem("mapWidth", mapWidthInput.value); - localStorage.setItem("mapHeight", mapHeightInput.value); + localStorage.setItem('mapWidth', mapWidthInput.value); + localStorage.setItem('mapHeight', mapHeightInput.value); } // change svg size on manual size change or window resize, do not change graph size function changeMapSize() { svgWidth = Math.min(+mapWidthInput.value, window.innerWidth); svgHeight = Math.min(+mapHeightInput.value, window.innerHeight); - svg.attr("width", svgWidth).attr("height", svgHeight); + svg.attr('width', svgWidth).attr('height', svgHeight); const maxWidth = Math.max(+mapWidthInput.value, graphWidth); const maxHeight = Math.max(+mapHeightInput.value, graphHeight); - zoom.translateExtent([[0, 0], [maxWidth, maxHeight]]); - landmass.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight); - oceanPattern.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight); - oceanLayers.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight); - fogging.selectAll("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight); - defs.select("mask#fog > rect").attr("width", maxWidth).attr("height", maxHeight); - texture.select("image").attr("width", maxWidth).attr("height", maxHeight); + zoom.translateExtent([ + [0, 0], + [maxWidth, maxHeight] + ]); + landmass.select('rect').attr('x', 0).attr('y', 0).attr('width', maxWidth).attr('height', maxHeight); + oceanPattern.select('rect').attr('x', 0).attr('y', 0).attr('width', maxWidth).attr('height', maxHeight); + oceanLayers.select('rect').attr('x', 0).attr('y', 0).attr('width', maxWidth).attr('height', maxHeight); + fogging.selectAll('rect').attr('x', 0).attr('y', 0).attr('width', maxWidth).attr('height', maxHeight); + defs.select('mask#fog > rect').attr('width', maxWidth).attr('height', maxHeight); + texture.select('image').attr('width', maxWidth).attr('height', maxHeight); fitScaleBar(); if (window.fitLegendBox) fitLegendBox(); @@ -172,21 +185,28 @@ function changeMapSize() { // just apply canvas size that was already set function applyMapSize() { - const zoomMin = +zoomExtentMin.value, zoomMax = +zoomExtentMax.value; + const zoomMin = +zoomExtentMin.value, + zoomMax = +zoomExtentMax.value; graphWidth = +mapWidthInput.value; graphHeight = +mapHeightInput.value; svgWidth = Math.min(graphWidth, window.innerWidth); svgHeight = Math.min(graphHeight, window.innerHeight); - svg.attr("width", svgWidth).attr("height", svgHeight); - zoom.translateExtent([[0, 0], [graphWidth, graphHeight]]).scaleExtent([zoomMin, zoomMax]).scaleTo(svg, zoomMin); + svg.attr('width', svgWidth).attr('height', svgHeight); + zoom + .translateExtent([ + [0, 0], + [graphWidth, graphHeight] + ]) + .scaleExtent([zoomMin, zoomMax]) + .scaleTo(svg, zoomMin); } function toggleFullscreen() { if (mapWidthInput.value != window.innerWidth || mapHeightInput.value != window.innerHeight) { mapWidthInput.value = window.innerWidth; mapHeightInput.value = window.innerHeight; - localStorage.removeItem("mapHeight"); - localStorage.removeItem("mapWidth"); + localStorage.removeItem('mapHeight'); + localStorage.removeItem('mapWidth'); } else { mapWidthInput.value = graphWidth; mapHeightInput.value = graphHeight; @@ -195,22 +215,32 @@ function toggleFullscreen() { } function toggleTranslateExtent(el) { - const on = el.dataset.on = +!(+el.dataset.on); - if (on) zoom.translateExtent([[-graphWidth/2, -graphHeight/2], [graphWidth*1.5, graphHeight*1.5]]); - else zoom.translateExtent([[0, 0], [graphWidth, graphHeight]]); + const on = (el.dataset.on = +!+el.dataset.on); + if (on) + zoom.translateExtent([ + [-graphWidth / 2, -graphHeight / 2], + [graphWidth * 1.5, graphHeight * 1.5] + ]); + else + zoom.translateExtent([ + [0, 0], + [graphWidth, graphHeight] + ]); } // add voice options -const voiceInterval = setInterval(function() { +const voiceInterval = setInterval(function () { const voices = speechSynthesis.getVoices(); - if (voices.length) clearInterval(voiceInterval); else return; + if (voices.length) clearInterval(voiceInterval); + else return; - const select = document.getElementById("speakerVoice"); + const select = document.getElementById('speakerVoice'); voices.forEach((voice, i) => { select.options.add(new Option(voice.name, i, false)); }); - if (stored("speakerVoice")) select.value = localStorage.getItem("speakerVoice"); // se voice to store - else select.value = voices.findIndex(voice => voice.lang === "en-US"); // or to first found English-US + if (stored('speakerVoice')) select.value = localStorage.getItem('speakerVoice'); + // se voice to store + else select.value = voices.findIndex((voice) => voice.lang === 'en-US'); // or to first found English-US }, 1000); function testSpeaker() { @@ -218,7 +248,7 @@ function testSpeaker() { const speaker = new SpeechSynthesisUtterance(text); const voices = speechSynthesis.getVoices(); if (voices.length) { - const voiceId = +document.getElementById("speakerVoice").value; + const voiceId = +document.getElementById('speakerVoice').value; speaker.voice = voices[voiceId]; } speechSynthesis.speak(speaker); @@ -226,36 +256,40 @@ function testSpeaker() { function generateMapWithSeed() { if (optionsSeed.value == seed) { - tip("The current map already has this seed", false, "error"); + tip('The current map already has this seed', false, 'error'); return; } regeneratePrompt(); } function showSeedHistoryDialog() { - const alert = mapHistory.map(function(h, i) { - const created = new Date(h.created).toLocaleTimeString(); - const button = ``; - return `
${i+1}. Seed: ${h.seed} ${button}. Size: ${h.width}x${h.height}. Template: ${h.template}. Created: ${created}
`; - }).join(""); + const alert = mapHistory + .map(function (h, i) { + const created = new Date(h.created).toLocaleTimeString(); + const button = ``; + return `
${i + 1}. Seed: ${h.seed} ${button}. Size: ${h.width}x${h.height}. Template: ${h.template}. Created: ${created}
`; + }) + .join(''); alertMessage.innerHTML = alert; - $("#alert").dialog({ - resizable: false, title: "Seed history", - width: fitContent(), position: {my: "center", at: "center", of: "svg"} + $('#alert').dialog({ + resizable: false, + title: 'Seed history', + width: fitContent(), + position: {my: 'center', at: 'center', of: 'svg'} }); } // generate map with historical seed function restoreSeed(id) { if (mapHistory[id].seed == seed) { - tip("The current map is already generated with this seed", null, "error"); + tip('The current map is already generated with this seed', null, 'error'); return; } optionsSeed.value = mapHistory[id].seed; mapWidthInput.value = mapHistory[id].width; mapHeightInput.value = mapHistory[id].height; templateInput.value = mapHistory[id].template; - if (locked("template")) unlock("template"); + if (locked('template')) unlock('template'); regeneratePrompt(); } @@ -266,18 +300,19 @@ function restoreDefaultZoomExtent() { } function copyMapURL() { - const locked = document.querySelectorAll("i.icon-lock").length; // check if some options are locked - const search = `?seed=${optionsSeed.value}&width=${graphWidth}&height=${graphHeight}${locked?'':'&options=default'}`; - navigator.clipboard.writeText(location.host+location.pathname+search) - .then(() => { - tip("Map URL is copied to clipboard", false, "success", 3000); - //window.history.pushState({}, null, search); - }) - .catch(err => tip("Could not copy URL: "+err, false, "error", 5000)); + const locked = document.querySelectorAll('i.icon-lock').length; // check if some options are locked + const search = `?seed=${optionsSeed.value}&width=${graphWidth}&height=${graphHeight}${locked ? '' : '&options=default'}`; + navigator.clipboard + .writeText(location.host + location.pathname + search) + .then(() => { + tip('Map URL is copied to clipboard', false, 'success', 3000); + //window.history.pushState({}, null, search); + }) + .catch((err) => tip('Could not copy URL: ' + err, false, 'error', 5000)); } function changeCellsDensity(value) { - const convert = v => { + const convert = (v) => { if (v == 1) return 1000; if (v == 2) return 2000; if (v == 3) return 5000; @@ -291,81 +326,81 @@ function changeCellsDensity(value) { if (v == 11) return 80000; if (v == 12) return 90000; if (v == 13) return 100000; - } + }; const cells = convert(value); - pointsInput.setAttribute("data-cells", cells); - pointsOutput.value = cells / 1000 + "K"; - pointsOutput.style.color = cells > 50000 ? "#b12117" : cells !== 10000 ? "#dfdf12" : "#053305"; + pointsInput.setAttribute('data-cells', cells); + pointsOutput.value = cells / 1000 + 'K'; + pointsOutput.style.color = cells > 50000 ? '#b12117' : cells !== 10000 ? '#dfdf12' : '#053305'; } function changeCultureSet() { const max = culturesSet.selectedOptions[0].dataset.max; - culturesInput.max = culturesOutput.max = max + culturesInput.max = culturesOutput.max = max; if (+culturesOutput.value > +max) culturesInput.value = culturesOutput.value = max; } function changeEmblemShape(emblemShape) { - const image = document.getElementById("emblemShapeImage"); + const image = document.getElementById('emblemShapeImage'); const shapePath = window.COArenderer && COArenderer.shieldPaths[emblemShape]; - shapePath ? image.setAttribute("d", shapePath) : image.removeAttribute("d"); + shapePath ? image.setAttribute('d', shapePath) : image.removeAttribute('d'); - const specificShape = ["culture", "state", "random"].includes(emblemShape) ? null : emblemShape; - if (emblemShape === "random") pack.cultures.filter(c => !c.removed).forEach(c => c.shield = Cultures.getRandomShield()); + const specificShape = ['culture', 'state', 'random'].includes(emblemShape) ? null : emblemShape; + if (emblemShape === 'random') pack.cultures.filter((c) => !c.removed).forEach((c) => (c.shield = Cultures.getRandomShield())); const rerenderCOA = (id, coa) => { const coaEl = document.getElementById(id); if (!coaEl) return; // not rendered coaEl.remove(); COArenderer.trigger(id, coa); - } + }; - pack.states.forEach(state => { - if (!state.i || state.removed || !state.coa || state.coa === "custom") return; + pack.states.forEach((state) => { + if (!state.i || state.removed || !state.coa || state.coa === 'custom') return; const newShield = specificShape || COA.getShield(state.culture, null); if (newShield === state.coa.shield) return; state.coa.shield = newShield; - rerenderCOA("stateCOA" + state.i, state.coa); + rerenderCOA('stateCOA' + state.i, state.coa); }); - pack.provinces.forEach(province => { - if (!province.i || province.removed || !province.coa || province.coa === "custom") return; + pack.provinces.forEach((province) => { + if (!province.i || province.removed || !province.coa || province.coa === 'custom') return; const culture = pack.cells.culture[province.center]; const newShield = specificShape || COA.getShield(culture, province.state); if (newShield === province.coa.shield) return; province.coa.shield = newShield; - rerenderCOA("provinceCOA" + province.i, province.coa); + rerenderCOA('provinceCOA' + province.i, province.coa); }); - pack.burgs.forEach(burg => { - if (!burg.i || burg.removed || !burg.coa || burg.coa === "custom") return; + pack.burgs.forEach((burg) => { + if (!burg.i || burg.removed || !burg.coa || burg.coa === 'custom') return; const newShield = specificShape || COA.getShield(burg.culture, burg.state); if (newShield === burg.coa.shield) return; - burg.coa.shield = newShield - rerenderCOA("burgCOA" + burg.i, burg.coa); + burg.coa.shield = newShield; + rerenderCOA('burgCOA' + burg.i, burg.coa); }); } function changeStatesNumber(value) { regionsInput.value = regionsOutput.value = value; - regionsOutput.style.color = +value ? null : "#b12117"; - burgLabels.select("#capitals").attr("data-size", Math.max(rn(6 - value / 20), 3)); - labels.select("#countries").attr("data-size", Math.max(rn(18 - value / 6), 4)); + regionsOutput.style.color = +value ? null : '#b12117'; + burgLabels.select('#capitals').attr('data-size', Math.max(rn(6 - value / 20), 3)); + labels.select('#countries').attr('data-size', Math.max(rn(18 - value / 6), 4)); } function changeBurgsNumberSlider(value) { - manorsOutput.value = value == 1000 ? "auto" : value; + manorsOutput.value = value == 1000 ? 'auto' : value; } function changeUIsize(value) { - if (isNaN(+value) || +value < .5) return; + if (isNaN(+value) || +value < 0.5) return; const max = getUImaxSize(); if (+value > max) value = max; uiSizeInput.value = uiSizeOutput.value = value; - document.getElementsByTagName("body")[0].style.fontSize = value * 11 + "px"; - document.getElementById("options").style.width = value * 300 + "px"; + document.getElementsByTagName('body')[0].style.fontSize = value * 11 + 'px'; + document.getElementById('options').style.width = value * 300 + 'px'; } function getUImaxSize() { @@ -381,62 +416,68 @@ function changeTooltipSize(value) { function changeDialogsTransparency(value) { transparencyInput.value = transparencyOutput.value = value; const alpha = (100 - +value) / 100; - const optionsColor = "rgba(164, 139, 149, " + alpha + ")"; - const dialogsColor = "rgba(255, 255, 255, " + alpha + ")"; - const optionButtonsColor = "rgba(145, 110, 127, " + Math.min(alpha + .3, 1) + ")"; - const optionLiColor = "rgba(153, 123, 137, " + Math.min(alpha + .3, 1) + ")"; - document.getElementById("options").style.backgroundColor = optionsColor; - document.getElementById("dialogs").style.backgroundColor = dialogsColor; - document.querySelectorAll(".tabcontent button").forEach(el => el.style.backgroundColor = optionButtonsColor); - document.querySelectorAll(".tabcontent li").forEach(el => el.style.backgroundColor = optionLiColor); - document.querySelectorAll("button.options").forEach(el => el.style.backgroundColor = optionLiColor); + const optionsColor = 'rgba(164, 139, 149, ' + alpha + ')'; + const dialogsColor = 'rgba(255, 255, 255, ' + alpha + ')'; + const optionButtonsColor = 'rgba(145, 110, 127, ' + Math.min(alpha + 0.3, 1) + ')'; + const optionLiColor = 'rgba(153, 123, 137, ' + Math.min(alpha + 0.3, 1) + ')'; + document.getElementById('options').style.backgroundColor = optionsColor; + document.getElementById('dialogs').style.backgroundColor = dialogsColor; + document.querySelectorAll('.tabcontent button').forEach((el) => (el.style.backgroundColor = optionButtonsColor)); + document.querySelectorAll('.tabcontent li').forEach((el) => (el.style.backgroundColor = optionLiColor)); + document.querySelectorAll('button.options').forEach((el) => (el.style.backgroundColor = optionLiColor)); } function changeZoomExtent(value) { - const min = Math.max(+zoomExtentMin.value, .01), max = Math.min(+zoomExtentMax.value, 200); + const min = Math.max(+zoomExtentMin.value, 0.01), + max = Math.min(+zoomExtentMax.value, 200); zoom.scaleExtent([min, max]); - const scale = Math.max(Math.min(+value, 200), .01); + const scale = Math.max(Math.min(+value, 200), 0.01); zoom.scaleTo(svg, scale); } // control stored options logic function applyStoredOptions() { - if (!localStorage.getItem("mapWidth") || !localStorage.getItem("mapHeight")) { + if (!localStorage.getItem('mapWidth') || !localStorage.getItem('mapHeight')) { mapWidthInput.value = window.innerWidth; mapHeightInput.value = window.innerHeight; } - if (localStorage.getItem("distanceUnit")) applyOption(distanceUnitInput, localStorage.getItem("distanceUnit")); - if (localStorage.getItem("heightUnit")) applyOption(heightUnit, localStorage.getItem("heightUnit")); + if (localStorage.getItem('distanceUnit')) applyOption(distanceUnitInput, localStorage.getItem('distanceUnit')); + if (localStorage.getItem('heightUnit')) applyOption(heightUnit, localStorage.getItem('heightUnit')); - for (let i=0; i < localStorage.length; i++) { - const stored = localStorage.key(i), value = localStorage.getItem(stored); - if (stored === "speakerVoice") continue; - const input = document.getElementById(stored+"Input") || document.getElementById(stored); - const output = document.getElementById(stored+"Output"); + for (let i = 0; i < localStorage.length; i++) { + const stored = localStorage.key(i), + value = localStorage.getItem(stored); + if (stored === 'speakerVoice') continue; + const input = document.getElementById(stored + 'Input') || document.getElementById(stored); + const output = document.getElementById(stored + 'Output'); if (input) input.value = value; if (output) output.value = value; lock(stored); // add saved style presets to options - if(stored.slice(0,5) === "style") applyOption(stylePreset, stored, stored.slice(5)); + if (stored.slice(0, 5) === 'style') applyOption(stylePreset, stored, stored.slice(5)); } - if (localStorage.getItem("winds")) options.winds = localStorage.getItem("winds").split(",").map(w => +w); - if (localStorage.getItem("military")) options.military = JSON.parse(localStorage.getItem("military")); + if (localStorage.getItem('winds')) + options.winds = localStorage + .getItem('winds') + .split(',') + .map((w) => +w); + if (localStorage.getItem('military')) options.military = JSON.parse(localStorage.getItem('military')); - changeDialogsTransparency(localStorage.getItem("transparency") || 5); - if (localStorage.getItem("tooltipSize")) changeTooltipSize(localStorage.getItem("tooltipSize")); - if (localStorage.getItem("regions")) changeStatesNumber(localStorage.getItem("regions")); + changeDialogsTransparency(localStorage.getItem('transparency') || 5); + if (localStorage.getItem('tooltipSize')) changeTooltipSize(localStorage.getItem('tooltipSize')); + if (localStorage.getItem('regions')) changeStatesNumber(localStorage.getItem('regions')); uiSizeInput.max = uiSizeOutput.max = getUImaxSize(); - if (localStorage.getItem("uiSize")) changeUIsize(localStorage.getItem("uiSize")); + if (localStorage.getItem('uiSize')) changeUIsize(localStorage.getItem('uiSize')); else changeUIsize(Math.max(Math.min(rn(mapWidthInput.value / 1280, 1), 2.5), 1)); // search params overwrite stored and default options const params = new URL(window.location.href).searchParams; - const width = +params.get("width"); - const height = +params.get("height"); + const width = +params.get('width'); + const height = +params.get('height'); if (width) mapWidthInput.value = width; if (height) mapHeightInput.value = height; } @@ -444,32 +485,36 @@ function applyStoredOptions() { // randomize options if randomization is allowed (not locked or options='default') function randomizeOptions() { Math.random = aleaPRNG(seed); // reset seed to initial one - const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options + const randomize = new URL(window.location.href).searchParams.get('options') === 'default'; // ignore stored options // 'Options' settings - if (randomize || !locked("template")) randomizeHeightmapTemplate(); - if (randomize || !locked("regions")) regionsInput.value = regionsOutput.value = gauss(15, 3, 2, 30); - if (randomize || !locked("provinces")) provincesInput.value = provincesOutput.value = gauss(20, 10, 20, 100); - if (randomize || !locked("manors")) {manorsInput.value = 1000; manorsOutput.value = "auto";} - if (randomize || !locked("religions")) religionsInput.value = religionsOutput.value = gauss(5, 2, 2, 10); - if (randomize || !locked("power")) powerInput.value = powerOutput.value = gauss(4, 2, 0, 10, 2); - if (randomize || !locked("neutral")) neutralInput.value = neutralOutput.value = rn(1 + Math.random(), 1); - if (randomize || !locked("cultures")) culturesInput.value = culturesOutput.value = gauss(12, 3, 5, 30); - if (randomize || !locked("culturesSet")) randomizeCultureSet(); + if (randomize || !locked('template')) randomizeHeightmapTemplate(); + if (randomize || !locked('regions')) regionsInput.value = regionsOutput.value = gauss(15, 3, 2, 30); + if (randomize || !locked('provinces')) provincesInput.value = provincesOutput.value = gauss(20, 10, 20, 100); + if (randomize || !locked('manors')) { + manorsInput.value = 1000; + manorsOutput.value = 'auto'; + } + if (randomize || !locked('religions')) religionsInput.value = religionsOutput.value = gauss(5, 2, 2, 10); + if (randomize || !locked('power')) powerInput.value = powerOutput.value = gauss(4, 2, 0, 10, 2); + if (randomize || !locked('neutral')) neutralInput.value = neutralOutput.value = rn(1 + Math.random(), 1); + if (randomize || !locked('cultures')) culturesInput.value = culturesOutput.value = gauss(12, 3, 5, 30); + if (randomize || !locked('culturesSet')) randomizeCultureSet(); // 'Configure World' settings - if (randomize || !locked("prec")) precInput.value = precOutput.value = gauss(100, 40, 5, 500); - const tMax = 30, tMin = -30; // temperature extremes - if (randomize || !locked("temperatureEquator")) temperatureEquatorOutput.value = temperatureEquatorInput.value = rand(tMax - 10, tMax); - if (randomize || !locked("temperaturePole")) temperaturePoleOutput.value = temperaturePoleInput.value = rand(tMin, tMin + 30); + if (randomize || !locked('prec')) precInput.value = precOutput.value = gauss(100, 40, 5, 500); + const tMax = 30, + tMin = -30; // temperature extremes + if (randomize || !locked('temperatureEquator')) temperatureEquatorOutput.value = temperatureEquatorInput.value = rand(tMax - 10, tMax); + if (randomize || !locked('temperaturePole')) temperaturePoleOutput.value = temperaturePoleInput.value = rand(tMin, tMin + 30); // 'Units Editor' settings - const US = navigator.language === "en-US"; - const UK = navigator.language === "en-GB"; - if (randomize || !locked("distanceScale")) distanceScaleOutput.value = distanceScaleInput.value = gauss(3, 1, 1, 5); - if (!stored("distanceUnit")) distanceUnitInput.value = US || UK ? "mi" : "km"; - if (!stored("heightUnit")) heightUnit.value = US || UK ? "ft" : "m"; - if (!stored("temperatureScale")) temperatureScale.value = US ? "°F" : "°C"; + const US = navigator.language === 'en-US'; + const UK = navigator.language === 'en-GB'; + if (randomize || !locked('distanceScale')) distanceScaleOutput.value = distanceScaleInput.value = gauss(3, 1, 1, 5); + if (!stored('distanceUnit')) distanceUnitInput.value = US || UK ? 'mi' : 'km'; + if (!stored('heightUnit')) heightUnit.value = US || UK ? 'ft' : 'm'; + if (!stored('temperatureScale')) temperatureScale.value = US ? '°F' : '°C'; // World settings generateEra(); @@ -478,60 +523,70 @@ function randomizeOptions() { // select heightmap template pseudo-randomly function randomizeHeightmapTemplate() { const templates = { - "Volcano": 3, - "High Island": 22, - "Low Island": 9, - "Continents": 20, - "Archipelago": 25, - "Mediterranean":3, - "Peninsula": 3, - "Pangea": 5, - "Isthmus": 2, - "Atoll": 1, - "Shattered": 7 + Volcano: 3, + 'High Island': 22, + 'Low Island': 9, + Continents: 20, + Archipelago: 25, + Mediterranean: 3, + Peninsula: 3, + Pangea: 5, + Isthmus: 2, + Atoll: 1, + Shattered: 7 }; - document.getElementById("templateInput").value = rw(templates); + document.getElementById('templateInput').value = rw(templates); } // select culture set pseudo-randomly function randomizeCultureSet() { const sets = { - "world": 10, - "european": 10, - "oriental": 2, - "english": 5, - "antique": 3, - "highFantasy": 11, - "darkFantasy": 3, - "random": 1}; + world: 10, + european: 10, + oriental: 2, + english: 5, + antique: 3, + highFantasy: 11, + darkFantasy: 3, + random: 1 + }; culturesSet.value = rw(sets); changeCultureSet(); } // generate current year and era name function generateEra() { - if (!stored("year")) yearInput.value = rand(100, 2000); // current year - if (!stored("era")) eraInput.value = Names.getBaseShort(P(.7) ? 1 : rand(nameBases.length)) + " Era"; + if (!stored('year')) yearInput.value = rand(100, 2000); // current year + if (!stored('era')) eraInput.value = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + ' Era'; options.year = +yearInput.value; options.era = eraInput.value; - options.eraShort = options.era.split(" ").map(w => w[0].toUpperCase()).join(""); // short name for era + options.eraShort = options.era + .split(' ') + .map((w) => w[0].toUpperCase()) + .join(''); // short name for era } function regenerateEra() { - unlock("era"); - options.era = eraInput.value = Names.getBaseShort(P(.7) ? 1 : rand(nameBases.length)) + " Era"; - options.eraShort = options.era.split(" ").map(w => w[0].toUpperCase()).join(""); + unlock('era'); + options.era = eraInput.value = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + ' Era'; + options.eraShort = options.era + .split(' ') + .map((w) => w[0].toUpperCase()) + .join(''); } function changeYear() { if (!yearInput.value) return; - if (isNaN(+yearInput.value)) {tip("Current year should be a number", false, "error"); return;} + if (isNaN(+yearInput.value)) { + tip('Current year should be a number', false, 'error'); + return; + } options.year = +yearInput.value; } function changeEra() { if (!eraInput.value) return; - lock("era"); + lock('era'); options.era = eraInput.value; } @@ -542,56 +597,74 @@ function restoreDefaultOptions() { } // Sticked menu Options listeners -document.getElementById("sticked").addEventListener("click", function(event) { +document.getElementById('sticked').addEventListener('click', function (event) { const id = event.target.id; - if (id === "newMapButton") regeneratePrompt(); - else if (id === "saveButton") showSavePane(); - else if (id === "loadButton") showLoadPane(); - else if (id === "zoomReset") resetZoom(1000); + if (id === 'newMapButton') regeneratePrompt(); + else if (id === 'saveButton') showSavePane(); + else if (id === 'loadButton') showLoadPane(); + else if (id === 'zoomReset') resetZoom(1000); }); function regeneratePrompt() { - if (customization) {tip("New map cannot be generated when edit mode is active, please exit the mode and retry", false, "error"); return;} - const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes - if (workingTime < 5) {regenerateMap(); return;} + if (customization) return tip('New map cannot be generated when edit mode is active, please exit the mode and retry', false, 'error'); + const workingMinutes = (Date.now() - last(mapHistory).created) / 60000; + if (workingMinutes < 5) return regenerateMap(); - alertMessage.innerHTML = `Are you sure you want to generate a new map?
- All unsaved changes made to the current map will be lost`; - $("#alert").dialog({resizable: false, title: "Generate new map", - buttons: { - Cancel: function() {$(this).dialog("close");}, - Generate: function() {closeDialogs(); regenerateMap();} - } - }); + const message = 'Are you sure you want to generate a new map?
All unsaved changes made to the current map will be lost'; + const onConfirm = () => { + closeDialogs(); + regenerateMap(); + }; + confirmationDialog({title: 'Generate new map', message, confirm: 'Generate', onConfirm}); } function showSavePane() { - $("#saveMapData").dialog({title: "Save map", resizable: false, width: "27em", - position: {my: "center", at: "center", of: "svg"}, - buttons: {Close: function() {$(this).dialog("close");}} + $('#saveMapData').dialog({ + title: 'Save map', + resizable: false, + width: '27em', + position: {my: 'center', at: 'center', of: 'svg'}, + buttons: { + Close: function () { + $(this).dialog('close'); + } + } }); } // download map data as GeoJSON function saveGeoJSON() { alertMessage.innerHTML = `You can export map data in GeoJSON format used in GIS tools such as QGIS. - Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export", "wiki-page")} for guidance`; + Check out ${link('https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export', 'wiki-page')} for guidance`; - $("#alert").dialog({title: "GIS data export", resizable: false, width: "35em", position: {my: "center", at: "center", of: "svg"}, + $('#alert').dialog({ + title: 'GIS data export', + resizable: false, + width: '35em', + position: {my: 'center', at: 'center', of: 'svg'}, buttons: { Cells: saveGeoJSON_Cells, Routes: saveGeoJSON_Routes, Rivers: saveGeoJSON_Rivers, Markers: saveGeoJSON_Markers, - Close: function() {$(this).dialog("close");} + Close: function () { + $(this).dialog('close'); + } } }); } function showLoadPane() { - $("#loadMapData").dialog({title: "Load map", resizable: false, width: "17em", - position: {my: "center", at: "center", of: "svg"}, - buttons: {Close: function() {$(this).dialog("close");}} + $('#loadMapData').dialog({ + title: 'Load map', + resizable: false, + width: '17em', + position: {my: 'center', at: 'center', of: 'svg'}, + buttons: { + Close: function () { + $(this).dialog('close'); + } + } }); } @@ -601,86 +674,96 @@ function loadURL() {
Please note server should allow CORS for file to be loaded. If CORS is not allowed, save file to Dropbox and provide a direct link`; alertMessage.innerHTML = inner; - $("#alert").dialog({resizable: false, title: "Load map from URL", width: "27em", + $('#alert').dialog({ + resizable: false, + title: 'Load map from URL', + width: '27em', buttons: { - Load: function() { + Load: function () { const value = mapURL.value; - if (!pattern.test(value)) {tip("Please provide a valid URL", false, "error"); return;} + if (!pattern.test(value)) { + tip('Please provide a valid URL', false, 'error'); + return; + } loadMapFromURL(value); - $(this).dialog("close"); + $(this).dialog('close'); }, - Cancel: function() {$(this).dialog("close");} + Cancel: function () { + $(this).dialog('close'); + } } }); } // load map -document.getElementById("mapToLoad").addEventListener("change", function() { +document.getElementById('mapToLoad').addEventListener('change', function () { const fileToLoad = this.files[0]; - this.value = ""; + this.value = ''; closeDialogs(); uploadMap(fileToLoad); }); // View mode -viewMode.addEventListener("click", changeViewMode); +viewMode.addEventListener('click', changeViewMode); function changeViewMode(event) { const button = event.target; - if (button.tagName !== "BUTTON") return; - const pressed = button.classList.contains("pressed"); + if (button.tagName !== 'BUTTON') return; + const pressed = button.classList.contains('pressed'); enterStandardView(); - if (!pressed && button.id !== "viewStandard") { - viewStandard.classList.remove("pressed"); - button.classList.add("pressed"); + if (!pressed && button.id !== 'viewStandard') { + viewStandard.classList.remove('pressed'); + button.classList.add('pressed'); enter3dView(button.id); } } function enterStandardView() { - viewMode.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); - heightmap3DView.classList.remove("pressed"); - viewStandard.classList.add("pressed"); + viewMode.querySelectorAll('.pressed').forEach((button) => button.classList.remove('pressed')); + heightmap3DView.classList.remove('pressed'); + viewStandard.classList.add('pressed'); - if (!document.getElementById("canvas3d")) return; + if (!document.getElementById('canvas3d')) return; ThreeD.stop(); - document.getElementById("canvas3d").remove(); - if (options3dUpdate.offsetParent) $("#options3d").dialog("close"); - if (preview3d.offsetParent) $("#preview3d").dialog("close"); + document.getElementById('canvas3d').remove(); + if (options3dUpdate.offsetParent) $('#options3d').dialog('close'); + if (preview3d.offsetParent) $('#preview3d').dialog('close'); } async function enter3dView(type) { - const canvas = document.createElement("canvas"); - canvas.id = "canvas3d"; + const canvas = document.createElement('canvas'); + canvas.id = 'canvas3d'; canvas.dataset.type = type; - if (type === "heightmap3DView") { + if (type === 'heightmap3DView') { canvas.width = parseFloat(preview3d.style.width) || graphWidth / 3; canvas.height = canvas.width / (graphWidth / graphHeight); - canvas.style.display = "block"; + canvas.style.display = 'block'; } else { canvas.width = svgWidth; canvas.height = svgHeight; - canvas.style.position = "absolute"; - canvas.style.display = "none"; + canvas.style.position = 'absolute'; + canvas.style.display = 'none'; } const started = await ThreeD.create(canvas, type); if (!started) return; - canvas.style.display = "block"; + canvas.style.display = 'block'; canvas.onmouseenter = () => { - const help = "Left mouse to change angle, middle mouse / mousewheel to zoom, right mouse to pan. O to toggle options"; - +canvas.dataset.hovered > 2 ? tip("") : tip(help); - canvas.dataset.hovered = (+canvas.dataset.hovered|0) + 1; + const help = 'Left mouse to change angle, middle mouse / mousewheel to zoom, right mouse to pan. O to toggle options'; + +canvas.dataset.hovered > 2 ? tip('') : tip(help); + canvas.dataset.hovered = (+canvas.dataset.hovered | 0) + 1; }; - if (type === "heightmap3DView") { - document.getElementById("preview3d").appendChild(canvas); - $("#preview3d").dialog({ - title: "3D Preview", resizable: true, - position: {my: "left bottom", at: "left+10 bottom-20", of: "svg"}, - resizeStop: resize3d, close: enterStandardView + if (type === 'heightmap3DView') { + document.getElementById('preview3d').appendChild(canvas); + $('#preview3d').dialog({ + title: '3D Preview', + resizable: true, + position: {my: 'left bottom', at: 'left+10 bottom-20', of: 'svg'}, + resizeStop: resize3d, + close: enterStandardView }); } else document.body.insertBefore(canvas, optionsContainer); @@ -688,17 +771,22 @@ async function enter3dView(type) { } function resize3d() { - const canvas = document.getElementById("canvas3d"); + const canvas = document.getElementById('canvas3d'); canvas.width = parseFloat(preview3d.style.width); canvas.height = parseFloat(preview3d.style.height) - 2; ThreeD.redraw(); } function toggle3dOptions() { - if (options3dUpdate.offsetParent) {$("#options3d").dialog("close"); return;} - $("#options3d").dialog({ - title: "3D mode settings", resizable: false, width: fitContent(), - position: {my: "right top", at: "right-30 top+10", of: "svg", collision: "fit"} + if (options3dUpdate.offsetParent) { + $('#options3d').dialog('close'); + return; + } + $('#options3d').dialog({ + title: '3D mode settings', + resizable: false, + width: fitContent(), + position: {my: 'right top', at: 'right-30 top+10', of: 'svg', collision: 'fit'} }); updateValues(); @@ -706,30 +794,30 @@ function toggle3dOptions() { if (modules.options3d) return; modules.options3d = true; - document.getElementById("options3dUpdate").addEventListener("click", ThreeD.update); - document.getElementById("options3dSave").addEventListener("click", ThreeD.saveScreenshot); - document.getElementById("options3dOBJSave").addEventListener("click", ThreeD.saveOBJ); + document.getElementById('options3dUpdate').addEventListener('click', ThreeD.update); + document.getElementById('options3dSave').addEventListener('click', ThreeD.saveScreenshot); + document.getElementById('options3dOBJSave').addEventListener('click', ThreeD.saveOBJ); - document.getElementById("options3dScaleRange").addEventListener("input", changeHeightScale); - document.getElementById("options3dScaleNumber").addEventListener("change", changeHeightScale); - document.getElementById("options3dLightnessRange").addEventListener("input", changeLightness); - document.getElementById("options3dLightnessNumber").addEventListener("change", changeLightness); - document.getElementById("options3dSunX").addEventListener("change", changeSunPosition); - document.getElementById("options3dSunY").addEventListener("change", changeSunPosition); - document.getElementById("options3dSunZ").addEventListener("change", changeSunPosition); - document.getElementById("options3dMeshRotationRange").addEventListener("input", changeRotation); - document.getElementById("options3dMeshRotationNumber").addEventListener("change", changeRotation); - document.getElementById("options3dGlobeRotationRange").addEventListener("input", changeRotation); - document.getElementById("options3dGlobeRotationNumber").addEventListener("change", changeRotation); - document.getElementById("options3dMeshSkyMode").addEventListener("change", toggleSkyMode); - document.getElementById("options3dMeshSky").addEventListener("input", changeColors); - document.getElementById("options3dMeshWater").addEventListener("input", changeColors); - document.getElementById("options3dGlobeResolution").addEventListener("change", changeResolution); + document.getElementById('options3dScaleRange').addEventListener('input', changeHeightScale); + document.getElementById('options3dScaleNumber').addEventListener('change', changeHeightScale); + document.getElementById('options3dLightnessRange').addEventListener('input', changeLightness); + document.getElementById('options3dLightnessNumber').addEventListener('change', changeLightness); + document.getElementById('options3dSunX').addEventListener('change', changeSunPosition); + document.getElementById('options3dSunY').addEventListener('change', changeSunPosition); + document.getElementById('options3dSunZ').addEventListener('change', changeSunPosition); + document.getElementById('options3dMeshRotationRange').addEventListener('input', changeRotation); + document.getElementById('options3dMeshRotationNumber').addEventListener('change', changeRotation); + document.getElementById('options3dGlobeRotationRange').addEventListener('input', changeRotation); + document.getElementById('options3dGlobeRotationNumber').addEventListener('change', changeRotation); + document.getElementById('options3dMeshSkyMode').addEventListener('change', toggleSkyMode); + document.getElementById('options3dMeshSky').addEventListener('input', changeColors); + document.getElementById('options3dMeshWater').addEventListener('input', changeColors); + document.getElementById('options3dGlobeResolution').addEventListener('change', changeResolution); function updateValues() { - const globe = document.getElementById("canvas3d").dataset.type === "viewGlobe"; - options3dMesh.style.display = globe ? "none" : "block"; - options3dGlobe.style.display = globe ? "block" : "none"; + const globe = document.getElementById('canvas3d').dataset.type === 'viewGlobe'; + options3dMesh.style.display = globe ? 'none' : 'block'; + options3dGlobe.style.display = globe ? 'block' : 'none'; options3dScaleRange.value = options3dScaleNumber.value = ThreeD.options.scale; options3dLightnessRange.value = options3dLightnessNumber.value = ThreeD.options.lightness * 100; options3dSunX.value = ThreeD.options.sun.x; @@ -738,7 +826,7 @@ function toggle3dOptions() { options3dMeshRotationRange.value = options3dMeshRotationNumber.value = ThreeD.options.rotateMesh; options3dGlobeRotationRange.value = options3dGlobeRotationNumber.value = ThreeD.options.rotateGlobe; options3dMeshSkyMode.value = ThreeD.options.extendedWater; - options3dColorSection.style.display = ThreeD.options.extendedWater ? "block" : "none"; + options3dColorSection.style.display = ThreeD.options.extendedWater ? 'block' : 'none'; options3dMeshSky.value = ThreeD.options.skyColor; options3dMeshWater.value = ThreeD.options.waterColor; options3dGlobeResolution.value = ThreeD.options.resolution; @@ -769,7 +857,7 @@ function toggle3dOptions() { function toggleSkyMode() { const hide = ThreeD.options.extendedWater; - options3dColorSection.style.display = hide ? "none" : "block"; + options3dColorSection.style.display = hide ? 'none' : 'block'; ThreeD.toggleSky(); } diff --git a/modules/ui/provinces-editor.js b/modules/ui/provinces-editor.js index 801ee003..c91ebe76 100644 --- a/modules/ui/provinces-editor.js +++ b/modules/ui/provinces-editor.js @@ -1,55 +1,64 @@ -"use strict"; +'use strict'; function editProvinces() { if (customization) return; - closeDialogs("#provincesEditor, .stable"); - if (!layerIsOn("toggleProvinces")) toggleProvinces(); - if (!layerIsOn("toggleBorders")) toggleBorders(); - if (layerIsOn("toggleStates")) toggleStates(); - if (layerIsOn("toggleCultures")) toggleCultures(); + closeDialogs('#provincesEditor, .stable'); + if (!layerIsOn('toggleProvinces')) toggleProvinces(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + if (layerIsOn('toggleStates')) toggleStates(); + if (layerIsOn('toggleCultures')) toggleCultures(); - provs.selectAll("text").call(d3.drag().on("drag", dragLabel)).classed("draggable", true); - const body = document.getElementById("provincesBodySection"); + provs.selectAll('text').call(d3.drag().on('drag', dragLabel)).classed('draggable', true); + const body = document.getElementById('provincesBodySection'); refreshProvincesEditor(); if (modules.editProvinces) return; modules.editProvinces = true; - $("#provincesEditor").dialog({ - title: "Provinces Editor", resizable: false, width: fitContent(), close: closeProvincesEditor, - position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} + $('#provincesEditor').dialog({ + title: 'Provinces Editor', + resizable: false, + width: fitContent(), + close: closeProvincesEditor, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'} }); // add listeners - document.getElementById("provincesEditorRefresh").addEventListener("click", refreshProvincesEditor); - document.getElementById("provincesEditStyle").addEventListener("click", () => editStyle("provs")); - document.getElementById("provincesFilterState").addEventListener("change", provincesEditorAddLines); - document.getElementById("provincesPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("provincesChart").addEventListener("click", showChart); - document.getElementById("provincesToggleLabels").addEventListener("click", toggleLabels); - document.getElementById("provincesExport").addEventListener("click", downloadProvincesData); - document.getElementById("provincesRemoveAll").addEventListener("click", removeAllProvinces); - document.getElementById("provincesManually").addEventListener("click", enterProvincesManualAssignent); - document.getElementById("provincesManuallyApply").addEventListener("click", applyProvincesManualAssignent); - document.getElementById("provincesManuallyCancel").addEventListener("click", () => exitProvincesManualAssignment()); - document.getElementById("provincesAdd").addEventListener("click", enterAddProvinceMode); - document.getElementById("provincesRecolor").addEventListener("click", recolorProvinces); + document.getElementById('provincesEditorRefresh').addEventListener('click', refreshProvincesEditor); + document.getElementById('provincesEditStyle').addEventListener('click', () => editStyle('provs')); + document.getElementById('provincesFilterState').addEventListener('change', provincesEditorAddLines); + document.getElementById('provincesPercentage').addEventListener('click', togglePercentageMode); + document.getElementById('provincesChart').addEventListener('click', showChart); + document.getElementById('provincesToggleLabels').addEventListener('click', toggleLabels); + document.getElementById('provincesExport').addEventListener('click', downloadProvincesData); + document.getElementById('provincesRemoveAll').addEventListener('click', removeAllProvinces); + document.getElementById('provincesManually').addEventListener('click', enterProvincesManualAssignent); + document.getElementById('provincesManuallyApply').addEventListener('click', applyProvincesManualAssignent); + document.getElementById('provincesManuallyCancel').addEventListener('click', () => exitProvincesManualAssignment()); + document.getElementById('provincesAdd').addEventListener('click', enterAddProvinceMode); + document.getElementById('provincesRecolor').addEventListener('click', recolorProvinces); - body.addEventListener("click", function(ev) { + body.addEventListener('click', function (ev) { if (customization) return; - const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id; - if (cl.contains("fillRect")) changeFill(el); else - if (cl.contains("name")) editProvinceName(p); else - if (cl.contains("coaIcon")) editEmblem("province", "provinceCOA"+p, pack.provinces[p]); else - if (cl.contains("icon-star-empty")) capitalZoomIn(p); else - if (cl.contains("icon-flag-empty")) triggerIndependencePromps(p); else - if (cl.contains("culturePopulation")) changePopulation(p); else - if (cl.contains("icon-pin")) toggleFog(p, cl); else - if (cl.contains("icon-trash-empty")) removeProvince(p); + const el = ev.target, + cl = el.classList, + line = el.parentNode, + p = +line.dataset.id; + if (cl.contains('fillRect')) changeFill(el); + else if (cl.contains('name')) editProvinceName(p); + else if (cl.contains('coaIcon')) editEmblem('province', 'provinceCOA' + p, pack.provinces[p]); + else if (cl.contains('icon-star-empty')) capitalZoomIn(p); + else if (cl.contains('icon-flag-empty')) triggerIndependence(p); + else if (cl.contains('culturePopulation')) changePopulation(p); + else if (cl.contains('icon-pin')) toggleFog(p, cl); + else if (cl.contains('icon-trash-empty')) removeProvince(p); }); - body.addEventListener("change", function(ev) { - const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id; - if (cl.contains("cultureBase")) changeCapital(p, line, el.value); + body.addEventListener('change', function (ev) { + const el = ev.target, + cl = el.classList, + line = el.parentNode, + p = +line.dataset.id; + if (cl.contains('cultureBase')) changeCapital(p, line, el.value); }); function refreshProvincesEditor() { @@ -59,12 +68,14 @@ function editProvinces() { } function collectStatistics() { - const cells = pack.cells, provinces = pack.provinces, burgs = pack.burgs; - provinces.forEach(p => { + const cells = pack.cells, + provinces = pack.provinces, + burgs = pack.burgs; + provinces.forEach((p) => { if (!p.i || p.removed) return; p.area = p.rural = p.urban = 0; p.burgs = []; - if (p.burg && !burgs[p.burg] || burgs[p.burg].removed) p.burg = 0; + if ((p.burg && !burgs[p.burg]) || burgs[p.burg].removed) p.burg = 0; }); for (const i of cells.i) { @@ -78,32 +89,34 @@ function editProvinces() { provinces[p].burgs.push(cells.burg[i]); } - provinces.forEach(p => { + provinces.forEach((p) => { if (!p.i || p.removed) return; if (!p.burg && p.burgs.length) p.burg = p.burgs[0]; }); } function updateFilter() { - const stateFilter = document.getElementById("provincesFilterState"); + const stateFilter = document.getElementById('provincesFilterState'); const selectedState = stateFilter.value || 1; stateFilter.options.length = 0; // remove all options stateFilter.options.add(new Option(`all`, -1, false, selectedState == -1)); - const statesSorted = pack.states.filter(s => s.i && !s.removed).sort((a, b) => (a.name > b.name) ? 1 : -1); - statesSorted.forEach(s => stateFilter.options.add(new Option(s.name, s.i, false, s.i == selectedState))); + const statesSorted = pack.states.filter((s) => s.i && !s.removed).sort((a, b) => (a.name > b.name ? 1 : -1)); + statesSorted.forEach((s) => stateFilter.options.add(new Option(s.name, s.i, false, s.i == selectedState))); } // add line for each state function provincesEditorAddLines() { - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - const selectedState = +document.getElementById("provincesFilterState").value; - let filtered = pack.provinces.filter(p => p.i && !p.removed); // all valid burgs - if (selectedState != -1) filtered = filtered.filter(p => p.state === selectedState); // filtered by state - body.innerHTML = ""; - let lines = "", totalArea = 0, totalPopulation = 0; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + const selectedState = +document.getElementById('provincesFilterState').value; + let filtered = pack.provinces.filter((p) => p.i && !p.removed); // all valid burgs + if (selectedState != -1) filtered = filtered.filter((p) => p.state === selectedState); // filtered by state + body.innerHTML = ''; + let lines = '', + totalArea = 0, + totalPopulation = 0; for (const p of filtered) { - const area = p.area * (distanceScaleInput.value ** 2); + const area = p.area * distanceScaleInput.value ** 2; totalArea += area; const rural = p.rural * populationRate.value; const urban = p.urban * populationRate.value * urbanization.value; @@ -114,22 +127,28 @@ function editProvinces() { const stateName = pack.states[p.state].name; const capital = p.burg ? pack.burgs[p.burg].name : ''; const separable = p.burg && p.burg !== pack.states[p.state].capital; - const focused = defs.select("#fog #focusProvince"+p.i).size(); - COArenderer.trigger("provinceCOA"+p.i, p.coa); - lines += `
- + const focused = defs.select('#fog #focusProvince' + p.i).size(); + COArenderer.trigger('provinceCOA' + p.i, p.coa); + lines += `
+ - - + +
${si(area) + unit}
${si(population)}
- +
`; } @@ -142,54 +161,66 @@ function editProvinces() { provincesFooterArea.dataset.area = totalArea; provincesFooterPopulation.dataset.population = totalPopulation; - body.querySelectorAll("div.states").forEach(el => { - el.addEventListener("click", selectProvinceOnLineClick); - el.addEventListener("mouseenter", ev => provinceHighlightOn(ev)); - el.addEventListener("mouseleave", ev => provinceHighlightOff(ev)); + body.querySelectorAll('div.states').forEach((el) => { + el.addEventListener('click', selectProvinceOnLineClick); + el.addEventListener('mouseenter', (ev) => provinceHighlightOn(ev)); + el.addEventListener('mouseleave', (ev) => provinceHighlightOff(ev)); }); - if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();} + if (body.dataset.type === 'percentage') { + body.dataset.type = 'absolute'; + togglePercentageMode(); + } applySorting(provincesHeader); - $("#provincesEditor").dialog({width: fitContent()}); + $('#provincesEditor').dialog({width: fitContent()}); } function getCapitalOptions(burgs, capital) { - let options = ""; - burgs.forEach(b => options += ``); + let options = ''; + burgs.forEach((b) => (options += ``)); return options; } function provinceHighlightOn(event) { const province = +event.target.dataset.id; const el = body.querySelector(`div[data-id='${province}']`); - if (el) el.classList.add("active"); + if (el) el.classList.add('active'); - if (!layerIsOn("toggleProvinces")) return; + if (!layerIsOn('toggleProvinces')) return; if (customization) return; const animate = d3.transition().duration(2000).ease(d3.easeSinIn); - provs.select("#province"+province).raise().transition(animate).attr("stroke-width", 2.5).attr("stroke", "#d0240f"); + provs + .select('#province' + province) + .raise() + .transition(animate) + .attr('stroke-width', 2.5) + .attr('stroke', '#d0240f'); } function provinceHighlightOff(event) { const province = +event.target.dataset.id; const el = body.querySelector(`div[data-id='${province}']`); - if (el) el.classList.remove("active"); + if (el) el.classList.remove('active'); - if (!layerIsOn("toggleProvinces")) return; - provs.select("#province"+province).transition().attr("stroke-width", null).attr("stroke", null); + if (!layerIsOn('toggleProvinces')) return; + provs + .select('#province' + province) + .transition() + .attr('stroke-width', null) + .attr('stroke', null); } function changeFill(el) { - const currentFill = el.getAttribute("fill"); + const currentFill = el.getAttribute('fill'); const p = +el.parentNode.parentNode.dataset.id; - const callback = function(fill) { - el.setAttribute("fill", fill); + const callback = function (fill) { + el.setAttribute('fill', fill); pack.provinces[p].color = fill; - const g = provs.select("#provincesBody"); - g.select("#province"+p).attr("fill", fill); - g.select("#province-gap"+p).attr("stroke", fill); - } + const g = provs.select('#provincesBody'); + g.select('#province' + p).attr('fill', fill); + g.select('#province-gap' + p).attr('stroke', fill); + }; openPicker(currentFill, callback); } @@ -197,26 +228,24 @@ function editProvinces() { function capitalZoomIn(p) { const capital = pack.provinces[p].burg; const l = burgLabels.select("[data-id='" + capital + "']"); - const x = +l.attr("x"), y = +l.attr("y"); + const x = +l.attr('x'), + y = +l.attr('y'); zoomTo(x, y, 8, 2000); } - function triggerIndependencePromps(p) { - alertMessage.innerHTML = "Are you sure you want to declare province independence?
It will turn province into a new state"; - $("#alert").dialog({resizable: false, title: "Declare independence", - buttons: { - Declare: function() { - declareProvinceIndependence(p); - $(this).dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + function triggerIndependence(p) { + const message = 'Are you sure you want to declare province independence?
It will turn province into a new state'; + confirmationDialog({title: 'Declare independence', message, confirm: 'Declare', onConfirm: () => declareProvinceIndependence(p)}); } function declareProvinceIndependence(p) { - const states = pack.states, provinces = pack.provinces, cells = pack.cells; - if (provinces[p].burgs.some(b => pack.burgs[b].capital)) {tip("Cannot declare independence of a province having capital burg. Please change capital first", false, "error"); return;} + const states = pack.states, + provinces = pack.provinces, + cells = pack.cells; + if (provinces[p].burgs.some((b) => pack.burgs[b].capital)) { + tip('Cannot declare independence of a province having capital burg. Please change capital first', false, 'error'); + return; + } const oldState = pack.provinces[p].state; const newState = pack.states.length; @@ -225,10 +254,10 @@ function editProvinces() { const burg = provinces[p].burg; if (!burg) return; pack.burgs[burg].capital = 1; - moveBurgToGroup(burg, "cities"); + moveBurgToGroup(burg, 'cities'); // move all burgs to a new state - provinces[p].burgs.forEach(b => pack.burgs[b].state = newState); + provinces[p].burgs.forEach((b) => (pack.burgs[b].state = newState)); // difine new state attributes const center = pack.burgs[burg].cell; @@ -237,51 +266,56 @@ function editProvinces() { const color = getRandomColor(); const coa = provinces[p].coa; - const coaEl = document.getElementById("provinceCOA"+p); - if (coaEl) coaEl.id = "stateCOA"+newState; + const coaEl = document.getElementById('provinceCOA' + p); + if (coaEl) coaEl.id = 'stateCOA' + newState; emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove(); // update cells - cells.i.filter(i => cells.province[i] === p).forEach(i => { - cells.province[i] = 0; - cells.state[i] = newState; - }); + cells.i + .filter((i) => cells.province[i] === p) + .forEach((i) => { + cells.province[i] = 0; + cells.state[i] = newState; + }); // update diplomacy and reverse relations - const diplomacy = states.map(s => { - if (!s.i || s.removed) return "x"; + const diplomacy = states.map((s) => { + if (!s.i || s.removed) return 'x'; let relations = states[oldState].diplomacy[s.i]; // relations between Nth state and old overlord - if (s.i === oldState) relations = "Enemy"; // new state is Enemy to its old overlord - else if (relations === "Ally") relations = "Suspicion"; - else if (relations === "Friendly") relations = "Suspicion"; - else if (relations === "Suspicion") relations = "Neutral"; - else if (relations === "Enemy") relations = "Friendly"; - else if (relations === "Rival") relations = "Friendly"; - else if (relations === "Vassal") relations = "Suspicion"; - else if (relations === "Suzerain") relations = "Enemy"; + if (s.i === oldState) relations = 'Enemy'; + // new state is Enemy to its old overlord + else if (relations === 'Ally') relations = 'Suspicion'; + else if (relations === 'Friendly') relations = 'Suspicion'; + else if (relations === 'Suspicion') relations = 'Neutral'; + else if (relations === 'Enemy') relations = 'Friendly'; + else if (relations === 'Rival') relations = 'Friendly'; + else if (relations === 'Vassal') relations = 'Suspicion'; + else if (relations === 'Suzerain') relations = 'Enemy'; s.diplomacy.push(relations); return relations; }); - diplomacy.push("x"); + diplomacy.push('x'); states[0].diplomacy.push([`Independance declaration`, `${name} declared its independance from ${states[oldState].name}`]); // create new state - states.push({i:newState, name, diplomacy, provinces:[], color, expansionism:.5, capital:burg, type:"Generic", center, culture, military:[], alert:1, coa}); + states.push({i: newState, name, diplomacy, provinces: [], color, expansionism: 0.5, capital: burg, type: 'Generic', center, culture, military: [], alert: 1, coa}); BurgsAndStates.collectStatistics(); BurgsAndStates.defineStateForms([newState]); - if (layerIsOn("toggleProvinces")) toggleProvinces(); - if (!layerIsOn("toggleStates")) toggleStates(); else drawStates(); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); + if (layerIsOn('toggleProvinces')) toggleProvinces(); + if (!layerIsOn('toggleStates')) toggleStates(); + else drawStates(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); BurgsAndStates.drawStateLabels([newState, oldState]); // remove old province - unfog("focusProvince"+p); + unfog('focusProvince' + p); if (states[oldState].provinces.includes(p)) states[oldState].provinces.splice(states[oldState].provinces.indexOf(p), 1); - provinces[p] = {i:p, removed: true}; + provinces[p] = {i: p, removed: true}; // draw emblem - COArenderer.add("state", newState, coa, pack.states[newState].pole[0], pack.states[newState].pole[1]); + COArenderer.add('state', newState, coa, pack.states[newState].pole[0], pack.states[newState].pole[1]); closeDialogs(); editStates(); @@ -289,54 +323,66 @@ function editProvinces() { function changePopulation(province) { const p = pack.provinces[province]; - const cells = pack.cells.i.filter(i => pack.cells.province[i] === province); - if (!cells.length) {tip("Province does not have any cells, cannot change population", false, "error"); return;} + const cells = pack.cells.i.filter((i) => pack.cells.province[i] === province); + if (!cells.length) { + tip('Province does not have any cells, cannot change population', false, 'error'); + return; + } const rural = rn(p.rural * populationRate.value); const urban = rn(p.urban * populationRate.value * urbanization.value); const total = rural + urban; - const l = n => Number(n).toLocaleString(); + const l = (n) => Number(n).toLocaleString(); alertMessage.innerHTML = ` Rural: - Urban: + Urban:

Total population: ${l(total)} ⇒ ${l(total)} (100%)

`; - const update = function() { + const update = function () { const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber; if (isNaN(totalNew)) return; totalPop.innerHTML = l(totalNew); - totalPopPerc.innerHTML = rn(totalNew / total * 100); - } + totalPopPerc.innerHTML = rn((totalNew / total) * 100); + }; ruralPop.oninput = () => update(); urbanPop.oninput = () => update(); - $("#alert").dialog({ - resizable: false, title: "Change province population", width: "24em", buttons: { - Apply: function() {applyPopulationChange(); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");} - }, position: {my: "center", at: "center", of: "svg"} + $('#alert').dialog({ + resizable: false, + title: 'Change province population', + width: '24em', + buttons: { + Apply: function () { + applyPopulationChange(); + $(this).dialog('close'); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); function applyPopulationChange() { const ruralChange = ruralPop.value / rural; if (isFinite(ruralChange) && ruralChange !== 1) { - cells.forEach(i => pack.cells.pop[i] *= ruralChange); + cells.forEach((i) => (pack.cells.pop[i] *= ruralChange)); } if (!isFinite(ruralChange) && +ruralPop.value > 0) { const points = ruralPop.value / populationRate.value; const pop = rn(points / cells.length); - cells.forEach(i => pack.cells.pop[i] = pop); + cells.forEach((i) => (pack.cells.pop[i] = pop)); } const urbanChange = urbanPop.value / urban; if (isFinite(urbanChange) && urbanChange !== 1) { - p.burgs.forEach(b => pack.burgs[b].population = rn(pack.burgs[b].population * urbanChange, 4)); + p.burgs.forEach((b) => (pack.burgs[b].population = rn(pack.burgs[b].population * urbanChange, 4))); } if (!isFinite(urbanChange) && +urbanPop.value > 0) { const points = urbanPop.value / populationRate.value / urbanization.value; const population = rn(points / burgs.length, 4); - p.burgs.forEach(b => pack.burgs[b].population = population); + p.burgs.forEach((b) => (pack.burgs[b].population = population)); } refreshProvincesEditor(); @@ -344,103 +390,109 @@ function editProvinces() { } function toggleFog(p, cl) { - const path = provs.select("#province"+p).attr("d"), id = "focusProvince"+p; - cl.contains("inactive") ? fog(id, path) : unfog(id); - cl.toggle("inactive"); + const path = provs.select('#province' + p).attr('d'), + id = 'focusProvince' + p; + cl.contains('inactive') ? fog(id, path) : unfog(id); + cl.toggle('inactive'); } function removeProvince(p) { - alertMessage.innerHTML = `Are you sure you want to remove the province?
This action cannot be reverted`; - $("#alert").dialog({resizable: false, title: "Remove province", - buttons: { - Remove: function() { - pack.cells.province.forEach((province, i) => { - if(province === p) pack.cells.province[i] = 0; - }); - const s = pack.provinces[p].state, state = pack.states[s]; - if (state.provinces.includes(p)) state.provinces.splice(state.provinces.indexOf(p), 1); + const message = 'Are you sure you want to remove the province?
This action cannot be reverted'; + const onConfirm = () => { + pack.cells.province.forEach((province, i) => { + if (province === p) pack.cells.province[i] = 0; + }); + const s = pack.provinces[p].state; + const state = pack.states[s]; + if (state.provinces.includes(p)) state.provinces.splice(state.provinces.indexOf(p), 1); - unfog("focusProvince"+p); + unfog('focusProvince' + p); - const coaId = "provinceCOA" + p; - if (document.getElementById(coaId)) document.getElementById(coaId).remove(); - emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove(); + const coaId = 'provinceCOA' + p; + if (document.getElementById(coaId)) document.getElementById(coaId).remove(); + emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove(); - pack.provinces[p] = {i: p, removed: true}; + pack.provinces[p] = {i: p, removed: true}; - const g = provs.select("#provincesBody"); - g.select("#province"+p).remove(); - g.select("#province-gap"+p).remove(); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - refreshProvincesEditor(); - $(this).dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const g = provs.select('#provincesBody'); + g.select('#province' + p).remove(); + g.select('#province-gap' + p).remove(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + refreshProvincesEditor(); + }; + confirmationDialog({title: 'Remove province', message, confirm: 'Remove', onConfirm}); } function editProvinceName(province) { const p = pack.provinces[province]; - document.getElementById("provinceNameEditor").dataset.province = province; - document.getElementById("provinceNameEditorShort").value = p.name; - applyOption(provinceNameEditorSelectForm, p.formName) - document.getElementById("provinceNameEditorFull").value = p.fullName; + document.getElementById('provinceNameEditor').dataset.province = province; + document.getElementById('provinceNameEditorShort').value = p.name; + applyOption(provinceNameEditorSelectForm, p.formName); + document.getElementById('provinceNameEditorFull').value = p.fullName; - $("#provinceNameEditor").dialog({ - resizable: false, title: "Change province name", buttons: { - Apply: function() {applyNameChange(p); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");} - }, position: {my: "center", at: "center", of: "svg"} + $('#provinceNameEditor').dialog({ + resizable: false, + title: 'Change province name', + buttons: { + Apply: function () { + applyNameChange(p); + $(this).dialog('close'); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); if (modules.editProvinceName) return; modules.editProvinceName = true; // add listeners - document.getElementById("provinceNameEditorShortCulture").addEventListener("click", regenerateShortNameCuture); - document.getElementById("provinceNameEditorShortRandom").addEventListener("click", regenerateShortNameRandom); - document.getElementById("provinceNameEditorAddForm").addEventListener("click", addCustomForm); - document.getElementById("provinceNameEditorFullRegenerate").addEventListener("click", regenerateFullName); + document.getElementById('provinceNameEditorShortCulture').addEventListener('click', regenerateShortNameCuture); + document.getElementById('provinceNameEditorShortRandom').addEventListener('click', regenerateShortNameRandom); + document.getElementById('provinceNameEditorAddForm').addEventListener('click', addCustomForm); + document.getElementById('provinceNameEditorFullRegenerate').addEventListener('click', regenerateFullName); function regenerateShortNameCuture() { const province = +provinceNameEditor.dataset.province; const culture = pack.cells.culture[pack.provinces[province].center]; const name = Names.getState(Names.getCultureShort(culture), culture); - document.getElementById("provinceNameEditorShort").value = name; + document.getElementById('provinceNameEditorShort').value = name; } function regenerateShortNameRandom() { - const base = rand(nameBases.length-1); + const base = rand(nameBases.length - 1); const name = Names.getState(Names.getBase(base), undefined, base); - document.getElementById("provinceNameEditorShort").value = name; + document.getElementById('provinceNameEditorShort').value = name; } function addCustomForm() { const value = provinceNameEditorCustomForm.value; - const displayed = provinceNameEditorCustomForm.style.display === "inline-block"; - provinceNameEditorCustomForm.style.display = displayed ? "none" : "inline-block"; - provinceNameEditorSelectForm.style.display = displayed ? "inline-block" : "none"; + const displayed = provinceNameEditorCustomForm.style.display === 'inline-block'; + provinceNameEditorCustomForm.style.display = displayed ? 'none' : 'inline-block'; + provinceNameEditorSelectForm.style.display = displayed ? 'inline-block' : 'none'; if (displayed) applyOption(provinceNameEditorSelectForm, value); } function regenerateFullName() { - const short = document.getElementById("provinceNameEditorShort").value; - const form = document.getElementById("provinceNameEditorSelectForm").value; - document.getElementById("provinceNameEditorFull").value = getFullName(); + const short = document.getElementById('provinceNameEditorShort').value; + const form = document.getElementById('provinceNameEditorSelectForm').value; + document.getElementById('provinceNameEditorFull').value = getFullName(); function getFullName() { if (!form) return short; - if (!short && form) return "The " + form; - return short + " " + form; + if (!short && form) return 'The ' + form; + return short + ' ' + form; } } function applyNameChange(p) { - p.name = document.getElementById("provinceNameEditorShort").value; - p.formName = document.getElementById("provinceNameEditorSelectForm").value; - p.fullName = document.getElementById("provinceNameEditorFull").value; - provs.select("#provinceLabel"+p.i).text(p.name); + p.name = document.getElementById('provinceNameEditorShort').value; + p.formName = document.getElementById('provinceNameEditorSelectForm').value; + p.fullName = document.getElementById('provinceNameEditorFull').value; + provs.select('#provinceLabel' + p.i).text(p.name); refreshProvincesEditor(); } } @@ -452,33 +504,38 @@ function editProvinces() { } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; + if (body.dataset.type === 'absolute') { + body.dataset.type = 'percentage'; const totalArea = +provincesFooterArea.dataset.area; const totalPopulation = +provincesFooterPopulation.dataset.population; - body.querySelectorAll(":scope > div").forEach(function(el) { - el.querySelector(".biomeArea").innerHTML = rn(+el.dataset.area / totalArea * 100) + "%"; - el.querySelector(".culturePopulation").innerHTML = rn(+el.dataset.population / totalPopulation * 100) + "%"; + body.querySelectorAll(':scope > div').forEach(function (el) { + el.querySelector('.biomeArea').innerHTML = rn((+el.dataset.area / totalArea) * 100) + '%'; + el.querySelector('.culturePopulation').innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + '%'; }); } else { - body.dataset.type = "absolute"; + body.dataset.type = 'absolute'; provincesEditorAddLines(); } } function showChart() { // build hierarchy tree - const getColor = (s) => !s.i || s.removed || s.color[0] !== "#" ? "#666" : d3.color(s.color).darker(); - const states = pack.states.map(s => ({id: s.i, state: s.i ? 0 : null, color: getColor(s)})); - const provinces = pack.provinces.filter(p => p.i && !p.removed).map(p => { - return {id:p.i+states.length-1, i:p.i, state:p.state, color:p.color, - name:p.name, fullName:p.fullName, area:p.area, urban:p.urban, rural:p.rural} - }); + const getColor = (s) => (!s.i || s.removed || s.color[0] !== '#' ? '#666' : d3.color(s.color).darker()); + const states = pack.states.map((s) => ({id: s.i, state: s.i ? 0 : null, color: getColor(s)})); + const provinces = pack.provinces + .filter((p) => p.i && !p.removed) + .map((p) => { + return {id: p.i + states.length - 1, i: p.i, state: p.state, color: p.color, name: p.name, fullName: p.fullName, area: p.area, urban: p.urban, rural: p.rural}; + }); const data = states.concat(provinces); - const root = d3.stratify().parentId(d => d.state)(data).sum(d => d.area); + const root = d3 + .stratify() + .parentId((d) => d.state)(data) + .sum((d) => d.area); - const width = 300 + 300 * uiSizeOutput.value, height = 90 + 90 * uiSizeOutput.value; + const width = 300 + 300 * uiSizeOutput.value, + height = 90 + 90 * uiSizeOutput.value; const margin = {top: 10, right: 10, bottom: 0, left: 10}; const w = width - margin.left - margin.right; const h = height - margin.top - margin.bottom; @@ -492,32 +549,39 @@ function editProvinces() { `; alertMessage.innerHTML += `
`; - const svg = d3.select("#alertMessage").insert("svg", "#provinceInfo").attr("id", "provincesTree") - .attr("width", width).attr("height", height).attr("font-size", "10px"); - const graph = svg.append("g").attr("transform", `translate(10, 0)`); - document.getElementById("provincesTreeType").addEventListener("change", updateChart); + const svg = d3.select('#alertMessage').insert('svg', '#provinceInfo').attr('id', 'provincesTree').attr('width', width).attr('height', height).attr('font-size', '10px'); + const graph = svg.append('g').attr('transform', `translate(10, 0)`); + document.getElementById('provincesTreeType').addEventListener('change', updateChart); treeLayout(root); - const node = graph.selectAll("g").data(root.leaves()).enter() - .append("g").attr("data-id", d => d.data.i) - .on("mouseenter", d => showInfo(event, d)) - .on("mouseleave", d => hideInfo(event, d)); + const node = graph + .selectAll('g') + .data(root.leaves()) + .enter() + .append('g') + .attr('data-id', (d) => d.data.i) + .on('mouseenter', (d) => showInfo(event, d)) + .on('mouseleave', (d) => hideInfo(event, d)); function showInfo(ev, d) { - d3.select(ev.target).select("rect").classed("selected", 1); + d3.select(ev.target).select('rect').classed('selected', 1); const name = d.data.fullName; const state = pack.states[d.data.state].fullName; - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - const area = d.data.area * (distanceScaleInput.value ** 2) + unit; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + const area = d.data.area * distanceScaleInput.value ** 2 + unit; const rural = rn(d.data.rural * populationRate.value); const urban = rn(d.data.urban * populationRate.value * urbanization.value); - const value = provincesTreeType.value === "area" ? "Area: " + area - : provincesTreeType.value === "rural" ? "Rural population: " + si(rural) - : provincesTreeType.value === "urban" ? "Urban population: " + si(urban) - : "Population: " + si(rural + urban); + const value = + provincesTreeType.value === 'area' + ? 'Area: ' + area + : provincesTreeType.value === 'rural' + ? 'Rural population: ' + si(rural) + : provincesTreeType.value === 'urban' + ? 'Urban population: ' + si(urban) + : 'Population: ' + si(rural + urban); provinceInfo.innerHTML = `${name}. ${state}. ${value}`; provinceHighlightOn(ev); @@ -525,106 +589,123 @@ function editProvinces() { function hideInfo(ev) { provinceHighlightOff(ev); - if (!document.getElementById("provinceInfo")) return; - provinceInfo.innerHTML = "‍"; - d3.select(ev.target).select("rect").classed("selected", 0); + if (!document.getElementById('provinceInfo')) return; + provinceInfo.innerHTML = '‍'; + d3.select(ev.target).select('rect').classed('selected', 0); } - node.append("rect").attr("stroke", d => d.parent.data.color) - .attr("stroke-width", 1).attr("fill", d => d.data.color) - .attr("x", d => d.x0).attr("y", d => d.y0) - .attr("width", d => d.x1 - d.x0).attr("height", d => d.y1 - d.y0); + node + .append('rect') + .attr('stroke', (d) => d.parent.data.color) + .attr('stroke-width', 1) + .attr('fill', (d) => d.data.color) + .attr('x', (d) => d.x0) + .attr('y', (d) => d.y0) + .attr('width', (d) => d.x1 - d.x0) + .attr('height', (d) => d.y1 - d.y0); - node.append("text").attr("dx", ".2em").attr("dy", "1em") - .attr("x", d => d.x0).attr("y", d => d.y0); + node + .append('text') + .attr('dx', '.2em') + .attr('dy', '1em') + .attr('x', (d) => d.x0) + .attr('y', (d) => d.y0); function hideNonfittingLabels() { - node.select("text").each(function(d) { + node.select('text').each(function (d) { this.innerHTML = d.data.name; let b = this.getBBox(); - if (b.y + b.height > d.y1 + 1) this.innerHTML = ""; + if (b.y + b.height > d.y1 + 1) this.innerHTML = ''; - for(let i=0; i < 15 && b.width > 0 && b.x + b.width > d.x1; i++) { - if (this.innerHTML.length < 3) {this.innerHTML = ""; break;} - this.innerHTML = this.innerHTML.slice(0, -2) + "…"; + for (let i = 0; i < 15 && b.width > 0 && b.x + b.width > d.x1; i++) { + if (this.innerHTML.length < 3) { + this.innerHTML = ''; + break; + } + this.innerHTML = this.innerHTML.slice(0, -2) + '…'; b = this.getBBox(); } - }) - + }); } function updateChart() { - const value = this.value === "area" ? d => d.area - : this.value === "rural" ? d => d.rural - : this.value === "urban" ? d => d.urban - : d => d.rural + d.urban; + const value = this.value === 'area' ? (d) => d.area : this.value === 'rural' ? (d) => d.rural : this.value === 'urban' ? (d) => d.urban : (d) => d.rural + d.urban; root.sum(value); node.data(treeLayout(root).leaves()); - node.select("rect").transition().duration(1500) - .attr("x", d => d.x0).attr("y", d => d.y0) - .attr("width", d => d.x1 - d.x0).attr("height", d => d.y1 - d.y0); + node + .select('rect') + .transition() + .duration(1500) + .attr('x', (d) => d.x0) + .attr('y', (d) => d.y0) + .attr('width', (d) => d.x1 - d.x0) + .attr('height', (d) => d.y1 - d.y0); - node.select("text").transition().duration(1500) - .attr("x", d => d.x0).attr("y", d => d.y0); + node + .select('text') + .transition() + .duration(1500) + .attr('x', (d) => d.x0) + .attr('y', (d) => d.y0); setTimeout(hideNonfittingLabels, 2000); } - $("#alert").dialog({ - title: "Provinces chart", width: fitContent(), - position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {}, - close: () => {alertMessage.innerHTML = "";} + $('#alert').dialog({ + title: 'Provinces chart', + width: fitContent(), + position: {my: 'left bottom', at: 'left+10 bottom-10', of: 'svg'}, + buttons: {}, + close: () => { + alertMessage.innerHTML = ''; + } }); hideNonfittingLabels(); } function toggleLabels() { - const hidden = provs.select("#provinceLabels").style("display") === "none"; - provs.select("#provinceLabels").style("display", `${hidden ? "block" : "none"}`); - provs.attr("data-labels", +hidden); - provs.selectAll("text").call(d3.drag().on("drag", dragLabel)).classed("draggable", true); + const hidden = provs.select('#provinceLabels').style('display') === 'none'; + provs.select('#provinceLabels').style('display', `${hidden ? 'block' : 'none'}`); + provs.attr('data-labels', +hidden); + provs.selectAll('text').call(d3.drag().on('drag', dragLabel)).classed('draggable', true); } function enterProvincesManualAssignent() { - if (!layerIsOn("toggleProvinces")) toggleProvinces(); - if (!layerIsOn("toggleBorders")) toggleBorders(); + if (!layerIsOn('toggleProvinces')) toggleProvinces(); + if (!layerIsOn('toggleBorders')) toggleBorders(); // make province and state borders more visible - provinceBorders.select("path").attr("stroke", "#000").attr("stroke-width", .5); - stateBorders.select("path").attr("stroke", "#000").attr("stroke-width", 1.2); + provinceBorders.select('path').attr('stroke', '#000').attr('stroke-width', 0.5); + stateBorders.select('path').attr('stroke', '#000').attr('stroke-width', 1.2); customization = 11; - provs.select("g#provincesBody").append("g").attr("id", "temp"); - provs.select("g#provincesBody").append("g").attr("id", "centers") - .attr("fill", "none").attr("stroke", "#ff0000").attr("stroke-width", 1); + provs.select('g#provincesBody').append('g').attr('id', 'temp'); + provs.select('g#provincesBody').append('g').attr('id', 'centers').attr('fill', 'none').attr('stroke', '#ff0000').attr('stroke-width', 1); - document.querySelectorAll("#provincesBottom > *").forEach(el => el.style.display = "none"); - document.getElementById("provincesManuallyButtons").style.display = "inline-block"; + document.querySelectorAll('#provincesBottom > *').forEach((el) => (el.style.display = 'none')); + document.getElementById('provincesManuallyButtons').style.display = 'inline-block'; - provincesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); - provincesHeader.querySelector("div[data-sortby='state']").style.left = "7.7em"; - provincesFooter.style.display = "none"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); - $("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); + provincesEditor.querySelectorAll('.hide').forEach((el) => el.classList.add('hidden')); + provincesHeader.querySelector("div[data-sortby='state']").style.left = '7.7em'; + provincesFooter.style.display = 'none'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); + $('#provincesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); - tip("Click on a province to select, drag the circle to change province", true); - viewbox.style("cursor", "crosshair") - .on("click", selectProvinceOnMapClick) - .call(d3.drag().on("start", dragBrush)) - .on("touchmove mousemove", moveBrush); + tip('Click on a province to select, drag the circle to change province', true); + viewbox.style('cursor', 'crosshair').on('click', selectProvinceOnMapClick).call(d3.drag().on('start', dragBrush)).on('touchmove mousemove', moveBrush); - body.querySelector("div").classList.add("selected"); - selectProvince(+body.querySelector("div").dataset.id); + body.querySelector('div').classList.add('selected'); + selectProvince(+body.querySelector('div').dataset.id); } function selectProvinceOnLineClick() { if (customization !== 11) return; - if (this.parentNode.id !== "provincesBodySection") return; - body.querySelector("div.selected").classList.remove("selected"); - this.classList.add("selected"); + if (this.parentNode.id !== 'provincesBodySection') return; + body.querySelector('div.selected').classList.remove('selected'); + this.classList.add('selected'); selectProvince(+this.dataset.id); } @@ -633,27 +714,30 @@ function editProvinces() { const i = findCell(point[0], point[1]); if (pack.cells.h[i] < 20 || !pack.cells.state[i]) return; - const assigned = provs.select("g#temp").select("polygon[data-cell='"+i+"']"); - const province = assigned.size() ? +assigned.attr("data-province") : pack.cells.province[i]; + const assigned = provs.select('g#temp').select("polygon[data-cell='" + i + "']"); + const province = assigned.size() ? +assigned.attr('data-province') : pack.cells.province[i]; - const editorLine = body.querySelector("div[data-id='"+province+"']"); - if (!editorLine) {tip("You cannot select a province if it is not in the Editor list", false, "error"); return;} + const editorLine = body.querySelector("div[data-id='" + province + "']"); + if (!editorLine) { + tip('You cannot select a province if it is not in the Editor list', false, 'error'); + return; + } - body.querySelector("div.selected").classList.remove("selected"); - editorLine.classList.add("selected"); + body.querySelector('div.selected').classList.remove('selected'); + editorLine.classList.add('selected'); selectProvince(province); } function selectProvince(p) { - debug.selectAll("path.selected").remove(); - const path = provs.select("#province"+p).attr("d"); - debug.append("path").attr("class", "selected").attr("d", path); + debug.selectAll('path.selected').remove(); + const path = provs.select('#province' + p).attr('d'); + debug.append('path').attr('class', 'selected').attr('d', path); } function dragBrush() { const r = +provincesManuallyBrush.value; - d3.event.on("drag", () => { + d3.event.on('drag', () => { if (!d3.event.dx && !d3.event.dy) return; const p = d3.mouse(this); moveCircle(p[0], p[1], r); @@ -666,33 +750,32 @@ function editProvinces() { // change province within selection function changeForSelection(selection) { - const temp = provs.select("#temp"), centers = provs.select("#centers"); - const selected = body.querySelector("div.selected"); + const temp = provs.select('#temp'), + centers = provs.select('#centers'); + const selected = body.querySelector('div.selected'); const provinceNew = +selected.dataset.id; const state = pack.provinces[provinceNew].state; - const fill = pack.provinces[provinceNew].color || "#ffffff"; + const fill = pack.provinces[provinceNew].color || '#ffffff'; - selection.forEach(i => { + selection.forEach((i) => { if (!pack.cells.state[i] || pack.cells.state[i] !== state) return; - const exists = temp.select("polygon[data-cell='"+i+"']"); - const provinceOld = exists.size() ? +exists.attr("data-province") : pack.cells.province[i]; + const exists = temp.select("polygon[data-cell='" + i + "']"); + const provinceOld = exists.size() ? +exists.attr('data-province') : pack.cells.province[i]; if (provinceNew === provinceOld) return; if (i === pack.provinces[provinceOld].center) { - const center = centers.select("polygon[data-center='"+i+"']"); - if (!center.size()) centers.append("polygon").attr("data-center", i).attr("points", getPackPolygon(i)); - tip("Province center cannot be assigned to a different region. Please remove the province first", false, "error"); + const center = centers.select("polygon[data-center='" + i + "']"); + if (!center.size()) centers.append('polygon').attr('data-center', i).attr('points', getPackPolygon(i)); + tip('Province center cannot be assigned to a different region. Please remove the province first', false, 'error'); return; } // change of append new element if (exists.size()) { if (pack.cells.province[i] === provinceNew) exists.remove(); - else exists.attr("data-province", provinceNew).attr("fill", fill); + else exists.attr('data-province', provinceNew).attr('fill', fill); } else { - temp.append("polygon").attr("points", getPackPolygon(i)) - .attr("data-cell", i).attr("data-province", provinceNew) - .attr("fill", fill).attr("stroke", "#555"); + temp.append('polygon').attr('points', getPackPolygon(i)).attr('data-cell', i).attr('data-province', provinceNew).attr('fill', fill).attr('stroke', '#555'); } }); } @@ -705,61 +788,79 @@ function editProvinces() { } function applyProvincesManualAssignent() { - provs.select("#temp").selectAll("polygon").each(function() { - const i = +this.dataset.cell; - pack.cells.province[i] = +this.dataset.province;; - }); + provs + .select('#temp') + .selectAll('polygon') + .each(function () { + const i = +this.dataset.cell; + pack.cells.province[i] = +this.dataset.province; + }); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - if (!layerIsOn("toggleProvinces")) toggleProvinces(); else drawProvinces(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + if (!layerIsOn('toggleProvinces')) toggleProvinces(); + else drawProvinces(); exitProvincesManualAssignment(); refreshProvincesEditor(); } function exitProvincesManualAssignment(close) { customization = 0; - provs.select("#temp").remove(); - provs.select("#centers").remove(); + provs.select('#temp').remove(); + provs.select('#centers').remove(); removeCircle(); // restore borders style - provinceBorders.select("path").attr("stroke", null).attr("stroke-width", null); - stateBorders.select("path").attr("stroke", null).attr("stroke-width", null); - debug.selectAll("path.selected").remove(); + provinceBorders.select('path').attr('stroke', null).attr('stroke-width', null); + stateBorders.select('path').attr('stroke', null).attr('stroke-width', null); + debug.selectAll('path.selected').remove(); - document.querySelectorAll("#provincesBottom > *").forEach(el => el.style.display = "inline-block"); - document.getElementById("provincesManuallyButtons").style.display = "none"; + document.querySelectorAll('#provincesBottom > *').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('provincesManuallyButtons').style.display = 'none'; - provincesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden")); - provincesHeader.querySelector("div[data-sortby='state']").style.left = "22em"; - provincesFooter.style.display = "block"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if(!close) $("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); + provincesEditor.querySelectorAll('.hide:not(.show)').forEach((el) => el.classList.remove('hidden')); + provincesHeader.querySelector("div[data-sortby='state']").style.left = '22em'; + provincesFooter.style.display = 'block'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (!close) $('#provincesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); restoreDefaultEvents(); clearMainTip(); - const selected = body.querySelector("div.selected"); - if (selected) selected.classList.remove("selected"); + const selected = body.querySelector('div.selected'); + if (selected) selected.classList.remove('selected'); } function enterAddProvinceMode() { - if (this.classList.contains("pressed")) {exitAddProvinceMode(); return;}; + if (this.classList.contains('pressed')) { + exitAddProvinceMode(); + return; + } customization = 12; - this.classList.add("pressed"); - tip("Click on the map to place a new province center", true); - viewbox.style("cursor", "crosshair").on("click", addProvince); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); + this.classList.add('pressed'); + tip('Click on the map to place a new province center', true); + viewbox.style('cursor', 'crosshair').on('click', addProvince); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); } function addProvince() { - const cells = pack.cells, provinces = pack.provinces; + const cells = pack.cells, + provinces = pack.provinces; const point = d3.mouse(this); const center = findCell(point[0], point[1]); - if (cells.h[center] < 20) {tip("You cannot place province into the water. Please click on a land cell", false, "error"); return;} + if (cells.h[center] < 20) { + tip('You cannot place province into the water. Please click on a land cell', false, 'error'); + return; + } const oldProvince = cells.province[center]; - if (oldProvince && provinces[oldProvince].center === center) {tip("The cell is already a center of a different province. Select other cell", false, "error"); return;} + if (oldProvince && provinces[oldProvince].center === center) { + tip('The cell is already a center of a different province. Select other cell', false, 'error'); + return; + } const state = cells.state[center]; - if (!state) {tip("You cannot create a province in neutral lands. Please assign this land to a state first", false, "error"); return;} + if (!state) { + tip('You cannot create a province in neutral lands. Please assign this land to a state first', false, 'error'); + return; + } if (d3.event.shiftKey === false) exitAddProvinceMode(); @@ -768,32 +869,35 @@ function editProvinces() { const burg = cells.burg[center]; const c = cells.culture[center]; const name = burg ? pack.burgs[burg].name : Names.getState(Names.getCultureShort(c), c); - const formName = oldProvince ? provinces[oldProvince].formName : "Province"; - const fullName = name + " " + formName; - const stateColor = pack.states[state].color, rndColor = getRandomColor(); - const color = stateColor[0] === "#" ? d3.color(d3.interpolate(stateColor, rndColor)(.2)).hex() : rndColor; + const formName = oldProvince ? provinces[oldProvince].formName : 'Province'; + const fullName = name + ' ' + formName; + const stateColor = pack.states[state].color, + rndColor = getRandomColor(); + const color = stateColor[0] === '#' ? d3.color(d3.interpolate(stateColor, rndColor)(0.2)).hex() : rndColor; // generate emblem - const kinship = burg ? .8 : .4; + const kinship = burg ? 0.8 : 0.4; const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa; const type = BurgsAndStates.getType(center, parent.port); - const coa = COA.generate(parent, kinship, P(.1), type); + const coa = COA.generate(parent, kinship, P(0.1), type); coa.shield = COA.getShield(c, state); - COArenderer.add("province", province, coa, point[0], point[1]); + COArenderer.add('province', province, coa, point[0], point[1]); - provinces.push({i:province, state, center, burg, name, formName, fullName, color, coa}); + provinces.push({i: province, state, center, burg, name, formName, fullName, color, coa}); cells.province[center] = province; - cells.c[center].forEach(c => { + cells.c[center].forEach((c) => { if (cells.h[c] < 20 || cells.state[c] !== state) return; - if (provinces.find(p => !p.removed && p.center === c)) return; + if (provinces.find((p) => !p.removed && p.center === c)) return; cells.province[c] = province; }); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - if (!layerIsOn("toggleProvinces")) toggleProvinces(); else drawProvinces(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + if (!layerIsOn('toggleProvinces')) toggleProvinces(); + else drawProvinces(); collectStatistics(); - document.getElementById("provincesFilterState").value = state; + document.getElementById('provincesFilterState').value = state; provincesEditorAddLines(); } @@ -801,89 +905,84 @@ function editProvinces() { customization = 0; restoreDefaultEvents(); clearMainTip(); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if (provincesAdd.classList.contains("pressed")) provincesAdd.classList.remove("pressed"); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (provincesAdd.classList.contains('pressed')) provincesAdd.classList.remove('pressed'); } function recolorProvinces() { - const state = +document.getElementById("provincesFilterState").value; + const state = +document.getElementById('provincesFilterState').value; - pack.provinces.forEach(p => { + pack.provinces.forEach((p) => { if (!p || p.removed) return; if (state !== -1 && p.state !== state) return; const stateColor = pack.states[p.state].color; const rndColor = getRandomColor(); - p.color = stateColor[0] === "#" ? d3.color(d3.interpolate(stateColor, rndColor)(.2)).hex() : rndColor; + p.color = stateColor[0] === '#' ? d3.color(d3.interpolate(stateColor, rndColor)(0.2)).hex() : rndColor; }); - if (!layerIsOn("toggleProvinces")) toggleProvinces(); else drawProvinces(); + if (!layerIsOn('toggleProvinces')) toggleProvinces(); + else drawProvinces(); } function downloadProvincesData() { - const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value; - let data = "Id,Province,Form,State,Color,Capital,Area "+unit+",Total Population,Rural Population,Urban Population\n"; // headers + const unit = areaUnit.value === 'square' ? distanceUnitInput.value + '2' : areaUnit.value; + let data = 'Id,Province,Form,State,Color,Capital,Area ' + unit + ',Total Population,Rural Population,Urban Population\n'; // headers - body.querySelectorAll(":scope > div").forEach(function(el) { - let key = parseInt(el.dataset.id) - data += el.dataset.id + ","; - data += el.dataset.name + ","; - data += el.dataset.form + ","; - data += el.dataset.state + ","; - data += el.dataset.color + ","; - data += el.dataset.capital + ","; - data += el.dataset.area + ","; - data += el.dataset.population + ","; - data += `${Math.round(pack.provinces[key].rural*populationRate.value)},` - data += `${Math.round(pack.provinces[key].urban*populationRate.value * urbanization.value)}\n` + body.querySelectorAll(':scope > div').forEach(function (el) { + let key = parseInt(el.dataset.id); + data += el.dataset.id + ','; + data += el.dataset.name + ','; + data += el.dataset.form + ','; + data += el.dataset.state + ','; + data += el.dataset.color + ','; + data += el.dataset.capital + ','; + data += el.dataset.area + ','; + data += el.dataset.population + ','; + data += `${Math.round(pack.provinces[key].rural * populationRate.value)},`; + data += `${Math.round(pack.provinces[key].urban * populationRate.value * urbanization.value)}\n`; }); - const name = getFileName("Provinces") + ".csv"; + const name = getFileName('Provinces') + '.csv'; downloadFile(data, name); } function removeAllProvinces() { - alertMessage.innerHTML = `Are you sure you want to remove all provinces?
This action cannot be reverted`; - $("#alert").dialog({resizable: false, title: "Remove all provinces", - buttons: { - Remove: function() { - $(this).dialog("close"); + const message = `Are you sure you want to remove all provinces?
This action cannot be reverted`; + const onConfirm = () => { + // remove emblems + document.querySelectorAll("[id^='provinceCOA']").forEach((el) => el.remove()); + emblems.select('#provinceEmblems').selectAll('*').remove(); - // remove emblems - document.querySelectorAll("[id^='provinceCOA']").forEach(el => el.remove()); - emblems.select("#provinceEmblems").selectAll("*").remove(); + // remove data + pack.provinces = [0]; + pack.cells.province = new Uint16Array(pack.cells.i.length); + pack.states.forEach((s) => (s.provinces = [])); - // remove data - pack.provinces = [0]; - pack.cells.province = new Uint16Array(pack.cells.i.length); - pack.states.forEach(s => s.provinces = []); + unfog(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + provs.select('#provincesBody').remove(); + turnButtonOff('toggleProvinces'); - unfog(); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - provs.select("#provincesBody").remove(); - turnButtonOff("toggleProvinces"); - - provincesEditorAddLines(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + provincesEditorAddLines(); + }; + confirmationDialog({title: 'Remove all provinces', message, confirm: 'Remove', onConfirm}); } function dragLabel() { - const tr = parseTransform(this.getAttribute("transform")); - const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y; + const tr = parseTransform(this.getAttribute('transform')); + const x = +tr[0] - d3.event.x, + y = +tr[1] - d3.event.y; - d3.event.on("drag", function() { - const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`; - this.setAttribute("transform", transform); + d3.event.on('drag', function () { + const transform = `translate(${x + d3.event.x},${y + d3.event.y})`; + this.setAttribute('transform', transform); }); } function closeProvincesEditor() { - provs.selectAll("text").call(d3.drag().on("drag", null)).attr("class", null); - if (customization === 11) exitProvincesManualAssignment("close"); + provs.selectAll('text').call(d3.drag().on('drag', null)).attr('class', null); + if (customization === 11) exitProvincesManualAssignment('close'); if (customization === 12) exitAddProvinceMode(); } - } - diff --git a/modules/ui/regiment-editor.js b/modules/ui/regiment-editor.js index 881ce3bf..342ceed9 100644 --- a/modules/ui/regiment-editor.js +++ b/modules/ui/regiment-editor.js @@ -1,85 +1,95 @@ -"use strict"; +'use strict'; function editRegiment(selector) { if (customization) return; - closeDialogs(".stable"); - if (!layerIsOn("toggleMilitary")) toggleMilitary(); + closeDialogs('.stable'); + if (!layerIsOn('toggleMilitary')) toggleMilitary(); - armies.selectAll(":scope > g").classed("draggable", true); - armies.selectAll(":scope > g > g").call(d3.drag().on("drag", dragRegiment)); + armies.selectAll(':scope > g').classed('draggable', true); + armies.selectAll(':scope > g > g').call(d3.drag().on('drag', dragRegiment)); elSelected = selector ? document.querySelector(selector) : d3.event.target.parentElement; // select g element if (!pack.states[elSelected.dataset.state]) return; if (!regiment()) return; updateRegimentData(regiment()); drawBase(); - $("#regimentEditor").dialog({ - title: "Edit Regiment", resizable: false, close: closeEditor, - position: {my: "left top", at: "left+10 top+10", of: "#map"} + $('#regimentEditor').dialog({ + title: 'Edit Regiment', + resizable: false, + close: closeEditor, + position: {my: 'left top', at: 'left+10 top+10', of: '#map'} }); if (modules.editRegiment) return; modules.editRegiment = true; // add listeners - document.getElementById("regimentNameRestore").addEventListener("click", restoreName); - document.getElementById("regimentType").addEventListener("click", changeType); - document.getElementById("regimentName").addEventListener("change", changeName); - document.getElementById("regimentEmblem").addEventListener("input", changeEmblem); - document.getElementById("regimentEmblemSelect").addEventListener("click", selectEmblem); - document.getElementById("regimentAttack").addEventListener("click", toggleAttack); - document.getElementById("regimentRegenerateLegend").addEventListener("click", regenerateLegend); - document.getElementById("regimentLegend").addEventListener("click", editLegend); - document.getElementById("regimentSplit").addEventListener("click", splitRegiment); - document.getElementById("regimentAdd").addEventListener("click", toggleAdd); - document.getElementById("regimentAttach").addEventListener("click", toggleAttach); - document.getElementById("regimentRemove").addEventListener("click", removeRegiment); + document.getElementById('regimentNameRestore').addEventListener('click', restoreName); + document.getElementById('regimentType').addEventListener('click', changeType); + document.getElementById('regimentName').addEventListener('change', changeName); + document.getElementById('regimentEmblem').addEventListener('input', changeEmblem); + document.getElementById('regimentEmblemSelect').addEventListener('click', selectEmblem); + document.getElementById('regimentAttack').addEventListener('click', toggleAttack); + document.getElementById('regimentRegenerateLegend').addEventListener('click', regenerateLegend); + document.getElementById('regimentLegend').addEventListener('click', editLegend); + document.getElementById('regimentSplit').addEventListener('click', splitRegiment); + document.getElementById('regimentAdd').addEventListener('click', toggleAdd); + document.getElementById('regimentAttach').addEventListener('click', toggleAttach); + document.getElementById('regimentRemove').addEventListener('click', removeRegiment); // get regiment data element function regiment() { - return pack.states[elSelected.dataset.state].military.find(r => r.i == elSelected.dataset.id); + return pack.states[elSelected.dataset.state].military.find((r) => r.i == elSelected.dataset.id); } function updateRegimentData(regiment) { - document.getElementById("regimentType").className = regiment.n ? "icon-anchor" :"icon-users"; - document.getElementById("regimentName").value = regiment.name; - document.getElementById("regimentEmblem").value = regiment.icon; - const composition = document.getElementById("regimentComposition"); + document.getElementById('regimentType').className = regiment.n ? 'icon-anchor' : 'icon-users'; + document.getElementById('regimentName').value = regiment.name; + document.getElementById('regimentEmblem').value = regiment.icon; + const composition = document.getElementById('regimentComposition'); - composition.innerHTML = options.military.map(u => { - return `
+ composition.innerHTML = options.military + .map((u) => { + return `
${capitalize(u.name)}:
- - ${u.type}
` - }).join(""); + + ${u.type}
`; + }) + .join(''); - composition.querySelectorAll("input").forEach(el => el.addEventListener("change", changeUnit)); + composition.querySelectorAll('input').forEach((el) => el.addEventListener('change', changeUnit)); } function drawBase() { const reg = regiment(); const clr = pack.states[elSelected.dataset.state].color; - const base = viewbox.insert("g", "g#armies").attr("id", "regimentBase").attr("stroke-width", .3).attr("stroke", "#000").attr("cursor", "move"); - base.on("mouseenter", () => {tip("Regiment base. Drag to re-base the regiment", true);}).on("mouseleave", () => {tip('', true);}); + const base = viewbox.insert('g', 'g#armies').attr('id', 'regimentBase').attr('stroke-width', 0.3).attr('stroke', '#000').attr('cursor', 'move'); + base + .on('mouseenter', () => { + tip('Regiment base. Drag to re-base the regiment', true); + }) + .on('mouseleave', () => { + tip('', true); + }); - base.append("line").attr("x1", reg.bx).attr("y1", reg.by).attr("x2", reg.x).attr("y2", reg.y).attr("class", "dragLine"); - base.append("circle").attr("cx", reg.bx).attr("cy", reg.by).attr("r", 2).attr("fill", clr).call(d3.drag().on("drag", dragBase)); + base.append('line').attr('x1', reg.bx).attr('y1', reg.by).attr('x2', reg.x).attr('y2', reg.y).attr('class', 'dragLine'); + base.append('circle').attr('cx', reg.bx).attr('cy', reg.by).attr('r', 2).attr('fill', clr).call(d3.drag().on('drag', dragBase)); } function changeType() { const reg = regiment(); reg.n = +!reg.n; - document.getElementById("regimentType").className = reg.n ? "icon-anchor" :"icon-users"; + document.getElementById('regimentType').className = reg.n ? 'icon-anchor' : 'icon-users'; - const size = +armies.attr("box-size"); - const baseRect = elSelected.querySelectorAll("rect")[0]; - const iconRect = elSelected.querySelectorAll("rect")[1]; - const icon = elSelected.querySelector(".regimentIcon"); - const x = reg.n ? reg.x-size*2 : reg.x-size*3; - baseRect.setAttribute("x", x); - baseRect.setAttribute("width", reg.n ? size*4 : size*6); - iconRect.setAttribute("x", x - size*2); - icon.setAttribute("x", x - size); - elSelected.querySelector("text").innerHTML = Military.getTotal(reg); + const size = +armies.attr('box-size'); + const baseRect = elSelected.querySelectorAll('rect')[0]; + const iconRect = elSelected.querySelectorAll('rect')[1]; + const icon = elSelected.querySelector('.regimentIcon'); + const x = reg.n ? reg.x - size * 2 : reg.x - size * 3; + baseRect.setAttribute('x', x); + baseRect.setAttribute('width', reg.n ? size * 4 : size * 6); + iconRect.setAttribute('x', x - size * 2); + icon.setAttribute('x', x - size); + elSelected.querySelector('text').innerHTML = Military.getTotal(reg); } function changeName() { @@ -87,49 +97,64 @@ function editRegiment(selector) { } function restoreName() { - const reg = regiment(), regs = pack.states[elSelected.dataset.state].military; + const reg = regiment(), + regs = pack.states[elSelected.dataset.state].military; const name = Military.getName(reg, regs); - elSelected.dataset.name = reg.name = document.getElementById("regimentName").value = name; + elSelected.dataset.name = reg.name = document.getElementById('regimentName').value = name; } function selectEmblem() { - selectIcon(regimentEmblem.value, v => {regimentEmblem.value = v; changeEmblem()}); + selectIcon(regimentEmblem.value, (v) => { + regimentEmblem.value = v; + changeEmblem(); + }); } function changeEmblem() { - const emblem = document.getElementById("regimentEmblem").value; - regiment().icon = elSelected.querySelector(".regimentIcon").innerHTML = emblem; + const emblem = document.getElementById('regimentEmblem').value; + regiment().icon = elSelected.querySelector('.regimentIcon').innerHTML = emblem; } function changeUnit() { const u = this.dataset.u; const reg = regiment(); - reg.u[u] = (+this.value)||0; + reg.u[u] = +this.value || 0; reg.a = d3.sum(Object.values(reg.u)); - elSelected.querySelector("text").innerHTML = Military.getTotal(reg); + elSelected.querySelector('text').innerHTML = Military.getTotal(reg); if (militaryOverviewRefresh.offsetParent) militaryOverviewRefresh.click(); if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click(); } function splitRegiment() { - const reg = regiment(), u1 = reg.u; - const state = +elSelected.dataset.state, military = pack.states[state].military; - const i = last(military).i + 1, u2 = Object.assign({}, u1); // u clone + const reg = regiment(), + u1 = reg.u; + const state = +elSelected.dataset.state, + military = pack.states[state].military; + const i = last(military).i + 1, + u2 = Object.assign({}, u1); // u clone - Object.keys(u2).forEach(u => u2[u] = Math.floor(u2[u]/2)); // halved new reg + Object.keys(u2).forEach((u) => (u2[u] = Math.floor(u2[u] / 2))); // halved new reg const a = d3.sum(Object.values(u2)); // new reg total - if (!a) {tip("Not enough forces to split", false, "error"); return}; // nothing to add + if (!a) { + tip('Not enough forces to split', false, 'error'); + return; + } // nothing to add // update old regiment - Object.keys(u1).forEach(u => u1[u] = Math.ceil(u1[u]/2)); // halved old reg + Object.keys(u1).forEach((u) => (u1[u] = Math.ceil(u1[u] / 2))); // halved old reg reg.a = d3.sum(Object.values(u1)); // old reg total - regimentComposition.querySelectorAll("input").forEach(el => el.value = reg.u[el.dataset.u]||0); - elSelected.querySelector("text").innerHTML = Military.getTotal(reg); + regimentComposition.querySelectorAll('input').forEach((el) => (el.value = reg.u[el.dataset.u] || 0)); + elSelected.querySelector('text').innerHTML = Military.getTotal(reg); // create new regiment - const shift = +armies.attr("box-size") * 2; - const y = function(x, y) {do {y+=shift} while (military.find(r => r.x === x && r.y === y)); return y;} - const newReg = {a, cell:reg.cell, i, n:reg.n, u:u2, x:reg.x, y:y(reg.x, reg.y), bx:reg.bx, by:reg.by, state, icon: reg.icon}; + const shift = +armies.attr('box-size') * 2; + const y = function (x, y) { + do { + y += shift; + } while (military.find((r) => r.x === x && r.y === y)); + return y; + }; + const newReg = {a, cell: reg.cell, i, n: reg.n, u: u2, x: reg.x, y: y(reg.x, reg.y), bx: reg.bx, by: reg.by, state, icon: reg.icon}; newReg.name = Military.getName(newReg, military); military.push(newReg); Military.generateNote(newReg, pack.states[state]); // add legend @@ -139,24 +164,26 @@ function editRegiment(selector) { } function toggleAdd() { - document.getElementById("regimentAdd").classList.toggle("pressed"); - if (document.getElementById("regimentAdd").classList.contains("pressed")) { - viewbox.style("cursor", "crosshair").on("click", addRegimentOnClick); - tip("Click on map to create new regiment or fleet", true); + document.getElementById('regimentAdd').classList.toggle('pressed'); + if (document.getElementById('regimentAdd').classList.contains('pressed')) { + viewbox.style('cursor', 'crosshair').on('click', addRegimentOnClick); + tip('Click on map to create new regiment or fleet', true); } else { clearMainTip(); - viewbox.on("click", clicked).style("cursor", "default"); + viewbox.on('click', clicked).style('cursor', 'default'); } } function addRegimentOnClick() { const point = d3.mouse(this); const cell = findCell(point[0], point[1]); - const x = pack.cells.p[cell][0], y = pack.cells.p[cell][1]; - const state = +elSelected.dataset.state, military = pack.states[state].military; + const x = pack.cells.p[cell][0], + y = pack.cells.p[cell][1]; + const state = +elSelected.dataset.state, + military = pack.states[state].military; const i = military.length ? last(military).i + 1 : 0; const n = +(pack.cells.h[cell] < 20); // naval or land - const reg = {a:0, cell, i, n, u:{}, x, y, bx:x, by:y, state, icon:"🛡️"}; + const reg = {a: 0, cell, i, n, u: {}, x, y, bx: x, by: y, state, icon: '🛡️'}; reg.name = Military.getName(reg, military); military.push(reg); Military.generateNote(reg, pack.states[state]); // add legend @@ -166,92 +193,124 @@ function editRegiment(selector) { } function toggleAttack() { - document.getElementById("regimentAttack").classList.toggle("pressed"); - if (document.getElementById("regimentAttack").classList.contains("pressed")) { - viewbox.style("cursor", "crosshair").on("click", attackRegimentOnClick); - tip("Click on another regiment to initiate battle", true); - armies.selectAll(":scope > g").classed("draggable", false); + document.getElementById('regimentAttack').classList.toggle('pressed'); + if (document.getElementById('regimentAttack').classList.contains('pressed')) { + viewbox.style('cursor', 'crosshair').on('click', attackRegimentOnClick); + tip('Click on another regiment to initiate battle', true); + armies.selectAll(':scope > g').classed('draggable', false); } else { clearMainTip(); - armies.selectAll(":scope > g").classed("draggable", true); - viewbox.on("click", clicked).style("cursor", "default"); + armies.selectAll(':scope > g').classed('draggable', true); + viewbox.on('click', clicked).style('cursor', 'default'); } } function attackRegimentOnClick() { - const target = d3.event.target, regSelected = target.parentElement, army = regSelected.parentElement; - const oldState = +elSelected.dataset.state, newState = +regSelected.dataset.state; + const target = d3.event.target, + regSelected = target.parentElement, + army = regSelected.parentElement; + const oldState = +elSelected.dataset.state, + newState = +regSelected.dataset.state; - if (army.parentElement.id !== "armies") {tip("Please click on a regiment to attack", false, "error"); return;} - if (regSelected === elSelected) {tip("Regiment cannot attack itself", false, "error"); return;} - if (oldState === newState) {tip("Cannot attack fraternal regiment", false, "error"); return;} + if (army.parentElement.id !== 'armies') { + tip('Please click on a regiment to attack', false, 'error'); + return; + } + if (regSelected === elSelected) { + tip('Regiment cannot attack itself', false, 'error'); + return; + } + if (oldState === newState) { + tip('Cannot attack fraternal regiment', false, 'error'); + return; + } const attacker = regiment(); - const defender = pack.states[regSelected.dataset.state].military.find(r => r.i == regSelected.dataset.id); - if (!attacker.a || !defender.a) {tip("Regiment has no troops to battle", false, "error"); return;} + const defender = pack.states[regSelected.dataset.state].military.find((r) => r.i == regSelected.dataset.id); + if (!attacker.a || !defender.a) { + tip('Regiment has no troops to battle', false, 'error'); + return; + } // save initial position to temp attribute - attacker.px = attacker.x, attacker.py = attacker.y; - defender.px = defender.x, defender.py = defender.y; + (attacker.px = attacker.x), (attacker.py = attacker.y); + (defender.px = defender.x), (defender.py = defender.y); // move attacker to defender - Military.moveRegiment(attacker, defender.x, defender.y-8); + Military.moveRegiment(attacker, defender.x, defender.y - 8); // draw battle icon - const attack = d3.transition().delay(300).duration(700).ease(d3.easeSinInOut).on("end", () => new Battle(attacker, defender)); - svg.append("text").attr("x", window.innerWidth/2).attr("y", window.innerHeight/2) - .text("⚔️").attr("font-size", 0).attr("opacity", 1) - .style("dominant-baseline", "central").style("text-anchor", "middle") - .transition(attack).attr("font-size", 1000).attr("opacity", .2).remove(); + const attack = d3 + .transition() + .delay(300) + .duration(700) + .ease(d3.easeSinInOut) + .on('end', () => new Battle(attacker, defender)); + svg + .append('text') + .attr('x', window.innerWidth / 2) + .attr('y', window.innerHeight / 2) + .text('⚔️') + .attr('font-size', 0) + .attr('opacity', 1) + .style('dominant-baseline', 'central') + .style('text-anchor', 'middle') + .transition(attack) + .attr('font-size', 1000) + .attr('opacity', 0.2) + .remove(); clearMainTip(); - $("#regimentEditor").dialog("close"); + $('#regimentEditor').dialog('close'); } function toggleAttach() { - document.getElementById("regimentAttach").classList.toggle("pressed"); - if (document.getElementById("regimentAttach").classList.contains("pressed")) { - viewbox.style("cursor", "crosshair").on("click", attachRegimentOnClick); - tip("Click on another regiment to unite both regiments. The current regiment will be removed", true); - armies.selectAll(":scope > g").classed("draggable", false); + document.getElementById('regimentAttach').classList.toggle('pressed'); + if (document.getElementById('regimentAttach').classList.contains('pressed')) { + viewbox.style('cursor', 'crosshair').on('click', attachRegimentOnClick); + tip('Click on another regiment to unite both regiments. The current regiment will be removed', true); + armies.selectAll(':scope > g').classed('draggable', false); } else { clearMainTip(); - armies.selectAll(":scope > g").classed("draggable", true); - viewbox.on("click", clicked).style("cursor", "default"); + armies.selectAll(':scope > g').classed('draggable', true); + viewbox.on('click', clicked).style('cursor', 'default'); } } function attachRegimentOnClick() { - const target = d3.event.target, regSelected = target.parentElement, army = regSelected.parentElement; - const oldState = +elSelected.dataset.state, newState = +regSelected.dataset.state; + const target = d3.event.target, + regSelected = target.parentElement, + army = regSelected.parentElement; + const oldState = +elSelected.dataset.state, + newState = +regSelected.dataset.state; - if (army.parentElement.id !== "armies") {tip("Please click on a regiment", false, "error"); return;} - if (regSelected === elSelected) {tip("Cannot attach regiment to itself. Please click on another regiment", false, "error"); return;} + if (army.parentElement.id !== 'armies') return tip('Please click on a regiment', false, 'error'); + if (regSelected === elSelected) return tip('Cannot attach regiment to itself. Please click on another regiment', false, 'error'); const reg = regiment(); // reg to be attached - const sel = pack.states[newState].military.find(r => r.i == regSelected.dataset.id); // reg to attach to + const sel = pack.states[newState].military.find((r) => r.i == regSelected.dataset.id); // reg to attach to for (const unit of options.military) { const u = unit.name; - if (reg.u[u]) sel.u[u] ? sel.u[u] += reg.u[u] : sel.u[u] = reg.u[u]; + if (reg.u[u]) sel.u[u] ? (sel.u[u] += reg.u[u]) : (sel.u[u] = reg.u[u]); } sel.a = d3.sum(Object.values(sel.u)); // reg total - regSelected.querySelector("text").innerHTML = Military.getTotal(sel); // update selected reg total text + regSelected.querySelector('text').innerHTML = Military.getTotal(sel); // update selected reg total text // remove attached regiment const military = pack.states[oldState].military; military.splice(military.indexOf(reg), 1); - const index = notes.findIndex(n => n.id === elSelected.id); + const index = notes.findIndex((n) => n.id === elSelected.id); if (index != -1) notes.splice(index, 1); elSelected.remove(); if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click(); - $("#regimentEditor").dialog("close"); - editRegiment("#"+regSelected.id); + $('#regimentEditor').dialog('close'); + editRegiment('#' + regSelected.id); } function regenerateLegend() { - const index = notes.findIndex(n => n.id === elSelected.id); + const index = notes.findIndex((n) => n.id === elSelected.id); if (index != -1) notes.splice(index, 1); const s = pack.states[elSelected.dataset.state]; @@ -263,85 +322,83 @@ function editRegiment(selector) { } function removeRegiment() { - alertMessage.innerHTML = "Are you sure you want to remove the regiment?"; - $("#alert").dialog({resizable: false, title: "Remove regiment", - buttons: { - Remove: function() { - $(this).dialog("close"); - const military = pack.states[elSelected.dataset.state].military; - const regIndex = military.indexOf(regiment()); - if (regIndex === -1) return; - military.splice(regIndex, 1); + const message = 'Are you sure you want to remove the regiment?
This action cannot be reverted'; + const onConfirm = () => { + const military = pack.states[elSelected.dataset.state].military; + const regIndex = military.indexOf(regiment()); + if (regIndex === -1) return; + military.splice(regIndex, 1); - const index = notes.findIndex(n => n.id === elSelected.id); - if (index != -1) notes.splice(index, 1); - elSelected.remove(); + const index = notes.findIndex((n) => n.id === elSelected.id); + if (index != -1) notes.splice(index, 1); + elSelected.remove(); - if (militaryOverviewRefresh.offsetParent) militaryOverviewRefresh.click(); - if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click(); - $("#regimentEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + if (militaryOverviewRefresh.offsetParent) militaryOverviewRefresh.click(); + if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click(); + $('#regimentEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove regiment', message, confirm: 'Remove', onConfirm}); } function dragRegiment() { d3.select(this).raise(); d3.select(this.parentNode).raise(); - const reg = pack.states[this.dataset.state].military.find(r => r.i == this.dataset.id); - const size = +armies.attr("box-size"); + const reg = pack.states[this.dataset.state].military.find((r) => r.i == this.dataset.id); + const size = +armies.attr('box-size'); const w = reg.n ? size * 4 : size * 6; const h = size * 2; - const x1 = x => rn(x - w / 2, 2); - const y1 = y => rn(y - size, 2); + const x1 = (x) => rn(x - w / 2, 2); + const y1 = (y) => rn(y - size, 2); - const baseRect = this.querySelector("rect"); - const text = this.querySelector("text"); - const iconRect = this.querySelectorAll("rect")[1]; - const icon = this.querySelector(".regimentIcon"); + const baseRect = this.querySelector('rect'); + const text = this.querySelector('text'); + const iconRect = this.querySelectorAll('rect')[1]; + const icon = this.querySelector('.regimentIcon'); const self = elSelected === this; - const baseLine = viewbox.select("g#regimentBase > line"); + const baseLine = viewbox.select('g#regimentBase > line'); - d3.event.on("drag", function() { - const x = reg.x = d3.event.x, y = reg.y = d3.event.y; + d3.event.on('drag', function () { + const x = (reg.x = d3.event.x), + y = (reg.y = d3.event.y); - baseRect.setAttribute("x", x1(x)); - baseRect.setAttribute("y", y1(y)); - text.setAttribute("x", x); - text.setAttribute("y", y); - iconRect.setAttribute("x", x1(x)-h); - iconRect.setAttribute("y", y1(y)); - icon.setAttribute("x", x1(x)-size); - icon.setAttribute("y", y); - if (self) baseLine.attr("x2", x).attr("y2", y); + baseRect.setAttribute('x', x1(x)); + baseRect.setAttribute('y', y1(y)); + text.setAttribute('x', x); + text.setAttribute('y', y); + iconRect.setAttribute('x', x1(x) - h); + iconRect.setAttribute('y', y1(y)); + icon.setAttribute('x', x1(x) - size); + icon.setAttribute('y', y); + if (self) baseLine.attr('x2', x).attr('y2', y); }); } function dragBase() { - const baseLine = viewbox.select("g#regimentBase > line"); + const baseLine = viewbox.select('g#regimentBase > line'); const reg = regiment(); - d3.event.on("drag", function() { - this.setAttribute("cx", d3.event.x); - this.setAttribute("cy", d3.event.y); - baseLine.attr("x1", d3.event.x).attr("y1", d3.event.y); + d3.event.on('drag', function () { + this.setAttribute('cx', d3.event.x); + this.setAttribute('cy', d3.event.y); + baseLine.attr('x1', d3.event.x).attr('y1', d3.event.y); }); - d3.event.on("end", function() {reg.bx = d3.event.x; reg.by = d3.event.y;}); + d3.event.on('end', function () { + reg.bx = d3.event.x; + reg.by = d3.event.y; + }); } function closeEditor() { - armies.selectAll(":scope > g").classed("draggable", false); - armies.selectAll("g>g").call(d3.drag().on("drag", null)); - viewbox.selectAll("g#regimentBase").remove(); - document.getElementById("regimentAdd").classList.remove("pressed"); - document.getElementById("regimentAttack").classList.remove("pressed"); - document.getElementById("regimentAttach").classList.remove("pressed"); + armies.selectAll(':scope > g').classed('draggable', false); + armies.selectAll('g>g').call(d3.drag().on('drag', null)); + viewbox.selectAll('g#regimentBase').remove(); + document.getElementById('regimentAdd').classList.remove('pressed'); + document.getElementById('regimentAttack').classList.remove('pressed'); + document.getElementById('regimentAttach').classList.remove('pressed'); restoreDefaultEvents(); elSelected = null; } - -} \ No newline at end of file +} diff --git a/modules/ui/relief-editor.js b/modules/ui/relief-editor.js index 7cd2d1d2..594ea710 100644 --- a/modules/ui/relief-editor.js +++ b/modules/ui/relief-editor.js @@ -1,19 +1,21 @@ -"use strict"; +'use strict'; function editReliefIcon() { if (customization) return; - closeDialogs(".stable"); - if (!layerIsOn("toggleRelief")) toggleRelief(); + closeDialogs('.stable'); + if (!layerIsOn('toggleRelief')) toggleRelief(); - terrain.selectAll("use").call(d3.drag().on("drag", dragReliefIcon)).classed("draggable", true); + terrain.selectAll('use').call(d3.drag().on('drag', dragReliefIcon)).classed('draggable', true); elSelected = d3.select(d3.event.target); restoreEditMode(); updateReliefIconSelected(); updateReliefSizeInput(); - $("#reliefEditor").dialog({ - title: "Edit Relief Icons", resizable: false, width: "27em", - position: {my: "left top", at: "left+10 top+10", of: "#map"}, + $('#reliefEditor').dialog({ + title: 'Edit Relief Icons', + resizable: false, + width: '27em', + position: {my: 'left top', at: 'left+10 top+10', of: '#map'}, close: closeReliefEditor }); @@ -21,62 +23,63 @@ function editReliefIcon() { modules.editReliefIcon = true; // add listeners - document.getElementById("reliefIndividual").addEventListener("click", enterIndividualMode); - document.getElementById("reliefBulkAdd").addEventListener("click", enterBulkAddMode); - document.getElementById("reliefBulkRemove").addEventListener("click", enterBulkRemoveMode); + document.getElementById('reliefIndividual').addEventListener('click', enterIndividualMode); + document.getElementById('reliefBulkAdd').addEventListener('click', enterBulkAddMode); + document.getElementById('reliefBulkRemove').addEventListener('click', enterBulkRemoveMode); - document.getElementById("reliefSize").addEventListener("input", changeIconSize); - document.getElementById("reliefSizeNumber").addEventListener("input", changeIconSize); - document.getElementById("reliefEditorSet").addEventListener("change", changeIconsSet); - reliefIconsDiv.querySelectorAll("svg").forEach(el => el.addEventListener("click", changeIcon)); + document.getElementById('reliefSize').addEventListener('input', changeIconSize); + document.getElementById('reliefSizeNumber').addEventListener('input', changeIconSize); + document.getElementById('reliefEditorSet').addEventListener('change', changeIconsSet); + reliefIconsDiv.querySelectorAll('svg').forEach((el) => el.addEventListener('click', changeIcon)); - document.getElementById("reliefEditStyle").addEventListener("click", () => editStyle("terrain")); - document.getElementById("reliefCopy").addEventListener("click", copyIcon); - document.getElementById("reliefMoveFront").addEventListener("click", () => elSelected.raise()); - document.getElementById("reliefMoveBack").addEventListener("click", () => elSelected.lower()); - document.getElementById("reliefRemove").addEventListener("click", removeIcon); + document.getElementById('reliefEditStyle').addEventListener('click', () => editStyle('terrain')); + document.getElementById('reliefCopy').addEventListener('click', copyIcon); + document.getElementById('reliefMoveFront').addEventListener('click', () => elSelected.raise()); + document.getElementById('reliefMoveBack').addEventListener('click', () => elSelected.lower()); + document.getElementById('reliefRemove').addEventListener('click', removeIcon); function dragReliefIcon() { - const dx = +this.getAttribute("x") - d3.event.x; - const dy = +this.getAttribute("y") - d3.event.y; + const dx = +this.getAttribute('x') - d3.event.x; + const dy = +this.getAttribute('y') - d3.event.y; - d3.event.on("drag", function() { - const x = d3.event.x, y = d3.event.y; - this.setAttribute("x", dx+x); - this.setAttribute("y", dy+y); + d3.event.on('drag', function () { + const x = d3.event.x, + y = d3.event.y; + this.setAttribute('x', dx + x); + this.setAttribute('y', dy + y); }); } function restoreEditMode() { - if (!reliefTools.querySelector("button.pressed")) enterIndividualMode(); else - if (reliefBulkAdd.classList.contains("pressed")) enterBulkAddMode(); else - if (reliefBulkRemove.classList.contains("pressed")) enterBulkRemoveMode(); + if (!reliefTools.querySelector('button.pressed')) enterIndividualMode(); + else if (reliefBulkAdd.classList.contains('pressed')) enterBulkAddMode(); + else if (reliefBulkRemove.classList.contains('pressed')) enterBulkRemoveMode(); } function updateReliefIconSelected() { - const type = elSelected.attr("href") || elSelected.attr("data-type"); - const button = reliefIconsDiv.querySelector("svg[data-type='"+type+"']"); + const type = elSelected.attr('href') || elSelected.attr('data-type'); + const button = reliefIconsDiv.querySelector("svg[data-type='" + type + "']"); - reliefIconsDiv.querySelectorAll("svg.pressed").forEach(b => b.classList.remove("pressed")); - button.classList.add("pressed"); - reliefIconsDiv.querySelectorAll("div").forEach(b => b.style.display = "none"); - button.parentNode.style.display = "block"; + reliefIconsDiv.querySelectorAll('svg.pressed').forEach((b) => b.classList.remove('pressed')); + button.classList.add('pressed'); + reliefIconsDiv.querySelectorAll('div').forEach((b) => (b.style.display = 'none')); + button.parentNode.style.display = 'block'; reliefEditorSet.value = button.parentNode.dataset.type; } function updateReliefSizeInput() { - const size = +elSelected.attr("width"); + const size = +elSelected.attr('width'); reliefSize.value = reliefSizeNumber.value = rn(size); } function enterIndividualMode() { - reliefTools.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed")); - reliefIndividual.classList.add("pressed"); + reliefTools.querySelectorAll('button.pressed').forEach((b) => b.classList.remove('pressed')); + reliefIndividual.classList.add('pressed'); - reliefSizeDiv.style.display = "block"; - reliefRadiusDiv.style.display = "none"; - reliefSpacingDiv.style.display = "none"; - reliefIconsSeletionAny.style.display = "none"; + reliefSizeDiv.style.display = 'block'; + reliefRadiusDiv.style.display = 'none'; + reliefSpacingDiv.style.display = 'none'; + reliefIconsSeletionAny.style.display = 'none'; updateReliefSizeInput(); restoreDefaultEvents(); @@ -84,22 +87,23 @@ function editReliefIcon() { } function enterBulkAddMode() { - reliefTools.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed")); - reliefBulkAdd.classList.add("pressed"); + reliefTools.querySelectorAll('button.pressed').forEach((b) => b.classList.remove('pressed')); + reliefBulkAdd.classList.add('pressed'); - reliefSizeDiv.style.display = "block"; - reliefRadiusDiv.style.display = "block"; - reliefSpacingDiv.style.display = "block"; - reliefIconsSeletionAny.style.display = "none"; + reliefSizeDiv.style.display = 'block'; + reliefRadiusDiv.style.display = 'block'; + reliefSpacingDiv.style.display = 'block'; + reliefIconsSeletionAny.style.display = 'none'; - const pressedType = reliefIconsDiv.querySelector("svg.pressed"); - if (pressedType.id === "reliefIconsSeletionAny") { // in "any" is pressed, select first type - reliefIconsSeletionAny.classList.remove("pressed"); - reliefIconsDiv.querySelector("svg").classList.add("pressed"); + const pressedType = reliefIconsDiv.querySelector('svg.pressed'); + if (pressedType.id === 'reliefIconsSeletionAny') { + // in "any" is pressed, select first type + reliefIconsSeletionAny.classList.remove('pressed'); + reliefIconsDiv.querySelector('svg').classList.add('pressed'); } - viewbox.style("cursor", "crosshair").call(d3.drag().on("start", dragToAdd)).on("touchmove mousemove", moveBrush); - tip("Drag to place relief icons within radius", true); + viewbox.style('cursor', 'crosshair').call(d3.drag().on('start', dragToAdd)).on('touchmove mousemove', moveBrush); + tip('Drag to place relief icons within radius', true); } function moveBrush() { @@ -110,8 +114,11 @@ function editReliefIcon() { } function dragToAdd() { - const pressed = reliefIconsDiv.querySelector("svg.pressed"); - if (!pressed) {tip("Please select an icon", false, error); return;} + const pressed = reliefIconsDiv.querySelector('svg.pressed'); + if (!pressed) { + tip('Please select an icon', false, error); + return; + } const type = pressed.dataset.type; const r = +reliefRadiusNumber.value; @@ -121,19 +128,19 @@ function editReliefIcon() { // build a quadtree const tree = d3.quadtree(); const positions = []; - terrain.selectAll("use").each(function() { - const x = +this.getAttribute("x") + this.getAttribute("width") / 2; - const y = +this.getAttribute("y") + this.getAttribute("height") / 2; + terrain.selectAll('use').each(function () { + const x = +this.getAttribute('x') + this.getAttribute('width') / 2; + const y = +this.getAttribute('y') + this.getAttribute('height') / 2; tree.add([x, y, x]); const box = this.getBBox(); positions.push(box.y + box.height); }); - d3.event.on("drag", function() { + d3.event.on('drag', function () { const p = d3.mouse(this); moveCircle(p[0], p[1], r); - d3.range(Math.ceil(r/10)).forEach(function() { + d3.range(Math.ceil(r / 10)).forEach(function () { const a = Math.PI * 2 * Math.random(); const rad = r * Math.random(); const cx = p[0] + rad * Math.cos(a); @@ -142,83 +149,93 @@ function editReliefIcon() { if (tree.find(cx, cy, spacing)) return; // too close to existing icon if (pack.cells.h[findCell(cx, cy)] < 20) return; // on water cell - const h = rn(size / 2 * (Math.random() * .4 + .8), 2); - const x = rn(cx-h, 2); - const y = rn(cy-h, 2); + const h = rn((size / 2) * (Math.random() * 0.4 + 0.8), 2); + const x = rn(cx - h, 2); + const y = rn(cy - h, 2); const z = y + h * 2; - const s = rn(h*2, 2); + const s = rn(h * 2, 2); let nth = 1; - while (positions[nth] && z > positions[nth]) {nth++;} + while (positions[nth] && z > positions[nth]) { + nth++; + } tree.add([cx, cy]); positions.push(z); - terrain.insert("use", ":nth-child("+nth+")").attr("href", type) - .attr("x", x).attr("y", y).attr("width", s).attr("height", s); + terrain + .insert('use', ':nth-child(' + nth + ')') + .attr('href', type) + .attr('x', x) + .attr('y', y) + .attr('width', s) + .attr('height', s); }); - }); } function enterBulkRemoveMode() { - reliefTools.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed")); - reliefBulkRemove.classList.add("pressed"); + reliefTools.querySelectorAll('button.pressed').forEach((b) => b.classList.remove('pressed')); + reliefBulkRemove.classList.add('pressed'); - reliefSizeDiv.style.display = "none"; - reliefRadiusDiv.style.display = "block"; - reliefSpacingDiv.style.display = "none"; - reliefIconsSeletionAny.style.display = "inline-block"; + reliefSizeDiv.style.display = 'none'; + reliefRadiusDiv.style.display = 'block'; + reliefSpacingDiv.style.display = 'none'; + reliefIconsSeletionAny.style.display = 'inline-block'; - viewbox.style("cursor", "crosshair").call(d3.drag().on("start", dragToRemove)).on("touchmove mousemove", moveBrush);; - tip("Drag to remove relief icons in radius", true); + viewbox.style('cursor', 'crosshair').call(d3.drag().on('start', dragToRemove)).on('touchmove mousemove', moveBrush); + tip('Drag to remove relief icons in radius', true); } function dragToRemove() { - const pressed = reliefIconsDiv.querySelector("svg.pressed"); - if (!pressed) {tip("Please select an icon", false, error); return;} + const pressed = reliefIconsDiv.querySelector('svg.pressed'); + if (!pressed) { + tip('Please select an icon', false, error); + return; + } const r = +reliefRadiusNumber.value; const type = pressed.dataset.type; - const icons = type ? terrain.selectAll("use[href='"+type+"']") : terrain.selectAll("use"); + const icons = type ? terrain.selectAll("use[href='" + type + "']") : terrain.selectAll('use'); const tree = d3.quadtree(); - icons.each(function() { - const x = +this.getAttribute("x") + this.getAttribute("width") / 2; - const y = +this.getAttribute("y") + this.getAttribute("height") / 2; + icons.each(function () { + const x = +this.getAttribute('x') + this.getAttribute('width') / 2; + const y = +this.getAttribute('y') + this.getAttribute('height') / 2; tree.add([x, y, this]); }); - d3.event.on("drag", function() { + d3.event.on('drag', function () { const p = d3.mouse(this); moveCircle(p[0], p[1], r); - tree.findAll(p[0], p[1], r).forEach(f => f[2].remove()); + tree.findAll(p[0], p[1], r).forEach((f) => f[2].remove()); }); } function changeIconSize() { const size = +reliefSizeNumber.value; - if (!reliefIndividual.classList.contains("pressed")) return; + if (!reliefIndividual.classList.contains('pressed')) return; - const shift = (size - +elSelected.attr("width")) / 2; - elSelected.attr("width", size).attr("height", size); - const x = +elSelected.attr("x"), y = +elSelected.attr("y"); - elSelected.attr("x", x-shift).attr("y", y-shift); + const shift = (size - +elSelected.attr('width')) / 2; + elSelected.attr('width', size).attr('height', size); + const x = +elSelected.attr('x'), + y = +elSelected.attr('y'); + elSelected.attr('x', x - shift).attr('y', y - shift); } function changeIconsSet() { const set = reliefEditorSet.value; - reliefIconsDiv.querySelectorAll("div").forEach(b => b.style.display = "none"); - reliefIconsDiv.querySelector("div[data-type='" + set + "']").style.display = "block"; + reliefIconsDiv.querySelectorAll('div').forEach((b) => (b.style.display = 'none')); + reliefIconsDiv.querySelector("div[data-type='" + set + "']").style.display = 'block'; } function changeIcon() { - if (this.classList.contains("pressed")) return; + if (this.classList.contains('pressed')) return; - reliefIconsDiv.querySelectorAll("svg.pressed").forEach(b => b.classList.remove("pressed")) - this.classList.add("pressed"); + reliefIconsDiv.querySelectorAll('svg.pressed').forEach((b) => b.classList.remove('pressed')); + this.classList.add('pressed'); - if (reliefIndividual.classList.contains("pressed")) { + if (reliefIndividual.classList.contains('pressed')) { const type = this.dataset.type; - elSelected.attr("href", type); + elSelected.attr('href', type); } } @@ -226,35 +243,31 @@ function editReliefIcon() { const parent = elSelected.node().parentNode; const copy = elSelected.node().cloneNode(true); - let x = +elSelected.attr("x") - 3, y = +elSelected.attr("y") - 3; - while (parent.querySelector("[x='"+x+"']","[x='"+y+"']")) { - x -= 3; y -= 3; + let x = +elSelected.attr('x') - 3, + y = +elSelected.attr('y') - 3; + while (parent.querySelector("[x='" + x + "']", "[x='" + y + "']")) { + x -= 3; + y -= 3; } - copy.setAttribute("x", x); - copy.setAttribute("y", y); + copy.setAttribute('x', x); + copy.setAttribute('y', y); parent.insertBefore(copy, null); } function removeIcon() { - alertMessage.innerHTML = `Are you sure you want to remove the icon?`; - $("#alert").dialog({resizable: false, title: "Remove relief icon", - buttons: { - Remove: function() { - $(this).dialog("close"); - elSelected.remove(); - $("#reliefEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the relief icon?
This action cannot be reverted'; + const onConfirm = () => { + elSelected.remove(); + $('#reliefEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove relief icon', message, confirm: 'Remove', onConfirm}); } function closeReliefEditor() { - terrain.selectAll("use").call(d3.drag().on("drag", null)).classed("draggable", false); + terrain.selectAll('use').call(d3.drag().on('drag', null)).classed('draggable', false); removeCircle(); unselect(); clearMainTip(); } - } diff --git a/modules/ui/religions-editor.js b/modules/ui/religions-editor.js index 8161f15e..981d4d17 100644 --- a/modules/ui/religions-editor.js +++ b/modules/ui/religions-editor.js @@ -1,14 +1,14 @@ -"use strict"; +'use strict'; function editReligions() { if (customization) return; - closeDialogs("#religionsEditor, .stable"); - if (!layerIsOn("toggleReligions")) toggleReligions(); - if (layerIsOn("toggleCultures")) toggleCultures(); - if (layerIsOn("toggleStates")) toggleStates(); - if (layerIsOn("toggleBiomes")) toggleBiomes(); - if (layerIsOn("toggleProvinces")) toggleProvinces(); + closeDialogs('#religionsEditor, .stable'); + if (!layerIsOn('toggleReligions')) toggleReligions(); + if (layerIsOn('toggleCultures')) toggleCultures(); + if (layerIsOn('toggleStates')) toggleStates(); + if (layerIsOn('toggleBiomes')) toggleBiomes(); + if (layerIsOn('toggleProvinces')) toggleProvinces(); - const body = document.getElementById("religionsBody"); + const body = document.getElementById('religionsBody'); const animate = d3.transition().duration(1500).ease(d3.easeSinIn); refreshReligionsEditor(); drawReligionCenters(); @@ -16,23 +16,26 @@ function editReligions() { if (modules.editReligions) return; modules.editReligions = true; - $("#religionsEditor").dialog({ - title: "Religions Editor", resizable: false, width: fitContent(), close: closeReligionsEditor, - position: {my: "right top", at: "right-10 top+10", of: "svg"} + $('#religionsEditor').dialog({ + title: 'Religions Editor', + resizable: false, + width: fitContent(), + close: closeReligionsEditor, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg'} }); // add listeners - document.getElementById("religionsEditorRefresh").addEventListener("click", refreshReligionsEditor); - document.getElementById("religionsEditStyle").addEventListener("click", () => editStyle("relig")); - document.getElementById("religionsLegend").addEventListener("click", toggleLegend); - document.getElementById("religionsPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("religionsHeirarchy").addEventListener("click", showHierarchy); - document.getElementById("religionsExtinct").addEventListener("click", toggleExtinct); - document.getElementById("religionsManually").addEventListener("click", enterReligionsManualAssignent); - document.getElementById("religionsManuallyApply").addEventListener("click", applyReligionsManualAssignent); - document.getElementById("religionsManuallyCancel").addEventListener("click", () => exitReligionsManualAssignment()); - document.getElementById("religionsAdd").addEventListener("click", enterAddReligionMode); - document.getElementById("religionsExport").addEventListener("click", downloadReligionsData); + document.getElementById('religionsEditorRefresh').addEventListener('click', refreshReligionsEditor); + document.getElementById('religionsEditStyle').addEventListener('click', () => editStyle('relig')); + document.getElementById('religionsLegend').addEventListener('click', toggleLegend); + document.getElementById('religionsPercentage').addEventListener('click', togglePercentageMode); + document.getElementById('religionsHeirarchy').addEventListener('click', showHierarchy); + document.getElementById('religionsExtinct').addEventListener('click', toggleExtinct); + document.getElementById('religionsManually').addEventListener('click', enterReligionsManualAssignent); + document.getElementById('religionsManuallyApply').addEventListener('click', applyReligionsManualAssignent); + document.getElementById('religionsManuallyCancel').addEventListener('click', () => exitReligionsManualAssignment()); + document.getElementById('religionsAdd').addEventListener('click', enterAddReligionMode); + document.getElementById('religionsExport').addEventListener('click', downloadReligionsData); function refreshReligionsEditor() { religionsCollectStatistics(); @@ -40,8 +43,9 @@ function editReligions() { } function religionsCollectStatistics() { - const cells = pack.cells, religions = pack.religions; - religions.forEach(r => r.cells = r.area = r.rural = r.urban = 0); + const cells = pack.cells, + religions = pack.religions; + religions.forEach((r) => (r.cells = r.area = r.rural = r.urban = 0)); for (const i of cells.i) { if (cells.h[i] < 20) continue; @@ -55,30 +59,34 @@ function editReligions() { // add line for each religion function religionsEditorAddLines() { - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - let lines = "", totalArea = 0, totalPopulation = 0; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + let lines = '', + totalArea = 0, + totalPopulation = 0; for (const r of pack.religions) { if (r.removed) continue; - const area = r.area * (distanceScaleInput.value ** 2); + const area = r.area * distanceScaleInput.value ** 2; const rural = r.rural * populationRate.value; const urban = r.urban * populationRate.value * urbanization.value; const population = rn(rural + urban); - if (r.i && !r.cells && body.dataset.extinct !== "show") continue; // hide extinct religions + if (r.i && !r.cells && body.dataset.extinct !== 'show') continue; // hide extinct religions const populationTip = `Believers: ${si(population)}; Rural areas: ${si(rural)}; Urban areas: ${si(urban)}. Click to change`; totalArea += area; totalPopulation += population; if (r.i) { lines += `
- + data-population=${population} data-type=${r.type} data-form=${r.form} data-deity="${r.deity ? r.deity : ''}" data-expansionism=${r.expansionism}> + - +
${si(area) + unit}
@@ -104,94 +112,119 @@ function editReligions() { body.innerHTML = lines; // update footer - const valid = pack.religions.filter(r => r.i && !r.removed); - religionsOrganized.innerHTML = valid.filter(r => r.type === "Organized").length; - religionsHeresies.innerHTML = valid.filter(r => r.type === "Heresy").length; - religionsCults.innerHTML = valid.filter(r => r.type === "Cult").length; - religionsFolk.innerHTML = valid.filter(r => r.type === "Folk").length; + const valid = pack.religions.filter((r) => r.i && !r.removed); + religionsOrganized.innerHTML = valid.filter((r) => r.type === 'Organized').length; + religionsHeresies.innerHTML = valid.filter((r) => r.type === 'Heresy').length; + religionsCults.innerHTML = valid.filter((r) => r.type === 'Cult').length; + religionsFolk.innerHTML = valid.filter((r) => r.type === 'Folk').length; religionsFooterArea.innerHTML = si(totalArea) + unit; religionsFooterPopulation.innerHTML = si(totalPopulation); religionsFooterArea.dataset.area = totalArea; religionsFooterPopulation.dataset.population = totalPopulation; // add listeners - body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseenter", ev => religionHighlightOn(ev))); - body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseleave", ev => religionHighlightOff(ev))); - body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectReligionOnLineClick)); - body.querySelectorAll("rect.fillRect").forEach(el => el.addEventListener("click", religionChangeColor)); - body.querySelectorAll("div > input.religionName").forEach(el => el.addEventListener("input", religionChangeName)); - body.querySelectorAll("div > select.religionType").forEach(el => el.addEventListener("change", religionChangeType)); - body.querySelectorAll("div > input.religionForm").forEach(el => el.addEventListener("input", religionChangeForm)); - body.querySelectorAll("div > input.religionDeity").forEach(el => el.addEventListener("input", religionChangeDeity)); - body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.addEventListener("click", regenerateDeity)); - body.querySelectorAll("div > div.culturePopulation").forEach(el => el.addEventListener("click", changePopulation)); - body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", religionRemove)); + body.querySelectorAll('div.religions').forEach((el) => el.addEventListener('mouseenter', (ev) => religionHighlightOn(ev))); + body.querySelectorAll('div.religions').forEach((el) => el.addEventListener('mouseleave', (ev) => religionHighlightOff(ev))); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('click', selectReligionOnLineClick)); + body.querySelectorAll('rect.fillRect').forEach((el) => el.addEventListener('click', religionChangeColor)); + body.querySelectorAll('div > input.religionName').forEach((el) => el.addEventListener('input', religionChangeName)); + body.querySelectorAll('div > select.religionType').forEach((el) => el.addEventListener('change', religionChangeType)); + body.querySelectorAll('div > input.religionForm').forEach((el) => el.addEventListener('input', religionChangeForm)); + body.querySelectorAll('div > input.religionDeity').forEach((el) => el.addEventListener('input', religionChangeDeity)); + body.querySelectorAll('div > span.icon-arrows-cw').forEach((el) => el.addEventListener('click', regenerateDeity)); + body.querySelectorAll('div > div.culturePopulation').forEach((el) => el.addEventListener('click', changePopulation)); + body.querySelectorAll('div > span.icon-trash-empty').forEach((el) => el.addEventListener('click', religionRemove)); - if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();} + if (body.dataset.type === 'percentage') { + body.dataset.type = 'absolute'; + togglePercentageMode(); + } applySorting(religionsHeader); - $("#religionsEditor").dialog({width: fitContent()}); + $('#religionsEditor').dialog({width: fitContent()}); } function getTypeOptions(type) { - let options = ""; - const types = ["Folk", "Organized", "Cult", "Heresy"]; - types.forEach(t => options += ``); + let options = ''; + const types = ['Folk', 'Organized', 'Cult', 'Heresy']; + types.forEach((t) => (options += ``)); return options; } function religionHighlightOn(event) { const religion = +event.target.dataset.id; - const info = document.getElementById("religionInfo"); + const info = document.getElementById('religionInfo'); if (info) { - d3.select("#hierarchy").select("g[data-id='"+religion+"'] > path").classed("selected", 1); + d3.select('#hierarchy') + .select("g[data-id='" + religion + "'] > path") + .classed('selected', 1); const r = pack.religions[religion]; - const type = r.name.includes(r.type) ? "" - : r.type === "Folk" || r.type === "Organized" - ? ". " + r.type + " religion" : ". " + r.type; - const form = r.form === r.type || r.name.includes(r.form) ? "" : ". " + r.form; + const type = r.name.includes(r.type) ? '' : r.type === 'Folk' || r.type === 'Organized' ? '. ' + r.type + ' religion' : '. ' + r.type; + const form = r.form === r.type || r.name.includes(r.form) ? '' : '. ' + r.form; const rural = r.rural * populationRate.value; const urban = r.urban * populationRate.value * urbanization.value; - const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct"; + const population = rural + urban > 0 ? '. ' + si(rn(rural + urban)) + ' believers' : '. Extinct'; info.innerHTML = `${r.name}${type}${form}${population}`; - tip("Drag to change parent, drag to itself to move to the top level. Hold CTRL and click to change abbreviation"); + tip('Drag to change parent, drag to itself to move to the top level. Hold CTRL and click to change abbreviation'); } const el = body.querySelector(`div[data-id='${religion}']`); - if (el) el.classList.add("active"); + if (el) el.classList.add('active'); - if (!layerIsOn("toggleReligions")) return; + if (!layerIsOn('toggleReligions')) return; if (customization) return; - relig.select("#religion"+religion).raise().transition(animate).attr("stroke-width", 2.5).attr("stroke", "#c13119"); - debug.select("#religionsCenter"+religion).raise().transition(animate).attr("r", 8).attr("stroke-width", 2).attr("stroke", "#c13119"); + relig + .select('#religion' + religion) + .raise() + .transition(animate) + .attr('stroke-width', 2.5) + .attr('stroke', '#c13119'); + debug + .select('#religionsCenter' + religion) + .raise() + .transition(animate) + .attr('r', 8) + .attr('stroke-width', 2) + .attr('stroke', '#c13119'); } function religionHighlightOff(event) { const religion = +event.target.dataset.id; - const info = document.getElementById("religionInfo"); + const info = document.getElementById('religionInfo'); if (info) { - d3.select("#hierarchy").select("g[data-id='"+religion+"'] > path").classed("selected", 0); - info.innerHTML = "‍"; - tip(""); + d3.select('#hierarchy') + .select("g[data-id='" + religion + "'] > path") + .classed('selected', 0); + info.innerHTML = '‍'; + tip(''); } - const el = body.querySelector(`div[data-id='${religion}']`) - if (el) el.classList.remove("active"); + const el = body.querySelector(`div[data-id='${religion}']`); + if (el) el.classList.remove('active'); - relig.select("#religion"+religion).transition().attr("stroke-width", null).attr("stroke", null); - debug.select("#religionsCenter"+religion).transition().attr("r", 4).attr("stroke-width", 1.2).attr("stroke", null); + relig + .select('#religion' + religion) + .transition() + .attr('stroke-width', null) + .attr('stroke', null); + debug + .select('#religionsCenter' + religion) + .transition() + .attr('r', 4) + .attr('stroke-width', 1.2) + .attr('stroke', null); } function religionChangeColor() { const el = this; - const currentFill = el.getAttribute("fill"); + const currentFill = el.getAttribute('fill'); const religion = +el.parentNode.parentNode.dataset.id; - const callback = function(fill) { - el.setAttribute("fill", fill); + const callback = function (fill) { + el.setAttribute('fill', fill); pack.religions[religion].color = fill; - relig.select("#religion"+religion).attr("fill", fill); - debug.select("#religionsCenter"+religion).attr("fill", fill); - } + relig.select('#religion' + religion).attr('fill', fill); + debug.select('#religionsCenter' + religion).attr('fill', fill); + }; openPicker(currentFill, callback); } @@ -200,7 +233,10 @@ function editReligions() { const religion = +this.parentNode.dataset.id; this.parentNode.dataset.name = this.value; pack.religions[religion].name = this.value; - pack.religions[religion].code = abbreviate(this.value, pack.religions.map(c => c.code)); + pack.religions[religion].code = abbreviate( + this.value, + pack.religions.map((c) => c.code) + ); } function religionChangeType() { @@ -233,113 +269,131 @@ function editReligions() { function changePopulation() { const religion = +this.parentNode.dataset.id; const r = pack.religions[religion]; - if (!r.cells) {tip("Religion does not have any cells, cannot change population", false, "error"); return;} + if (!r.cells) { + tip('Religion does not have any cells, cannot change population', false, 'error'); + return; + } const rural = rn(r.rural * populationRate.value); const urban = rn(r.urban * populationRate.value * urbanization.value); const total = rural + urban; - const l = n => Number(n).toLocaleString(); - const burgs = pack.burgs.filter(b => !b.removed && pack.cells.religion[b.cell] === religion); + const l = (n) => Number(n).toLocaleString(); + const burgs = pack.burgs.filter((b) => !b.removed && pack.cells.religion[b.cell] === religion); alertMessage.innerHTML = `

Please note all population of religion territory is considered believers of this religion. It means believers number change will directly change population

Rural: - Urban: + Urban:

Total believers: ${l(total)} ⇒ ${l(total)} (100%)

`; - const update = function() { + const update = function () { const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber; if (isNaN(totalNew)) return; totalPop.innerHTML = l(totalNew); - totalPopPerc.innerHTML = rn(totalNew / total * 100); - } + totalPopPerc.innerHTML = rn((totalNew / total) * 100); + }; ruralPop.oninput = () => update(); urbanPop.oninput = () => update(); - $("#alert").dialog({ - resizable: false, title: "Change believers number", width: "24em", buttons: { - Apply: function() {applyPopulationChange(); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");} - }, position: {my: "center", at: "center", of: "svg"} + $('#alert').dialog({ + resizable: false, + title: 'Change believers number', + width: '24em', + buttons: { + Apply: function () { + applyPopulationChange(); + $(this).dialog('close'); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); function applyPopulationChange() { const ruralChange = ruralPop.value / rural; if (isFinite(ruralChange) && ruralChange !== 1) { - const cells = pack.cells.i.filter(i => pack.cells.religion[i] === religion); - cells.forEach(i => pack.cells.pop[i] *= ruralChange); + const cells = pack.cells.i.filter((i) => pack.cells.religion[i] === religion); + cells.forEach((i) => (pack.cells.pop[i] *= ruralChange)); } if (!isFinite(ruralChange) && +ruralPop.value > 0) { const points = ruralPop.value / populationRate.value; - const cells = pack.cells.i.filter(i => pack.cells.religion[i] === religion); + const cells = pack.cells.i.filter((i) => pack.cells.religion[i] === religion); const pop = rn(points / cells.length); - cells.forEach(i => pack.cells.pop[i] = pop); + cells.forEach((i) => (pack.cells.pop[i] = pop)); } const urbanChange = urbanPop.value / urban; if (isFinite(urbanChange) && urbanChange !== 1) { - burgs.forEach(b => b.population = rn(b.population * urbanChange, 4)); + burgs.forEach((b) => (b.population = rn(b.population * urbanChange, 4))); } if (!isFinite(urbanChange) && +urbanPop.value > 0) { const points = urbanPop.value / populationRate.value / urbanization.value; const population = rn(points / burgs.length, 4); - burgs.forEach(b => b.population = population); + burgs.forEach((b) => (b.population = population)); } refreshReligionsEditor(); } - } function religionRemove() { if (customization) return; const religion = +this.parentNode.dataset.id; - alertMessage.innerHTML = "Are you sure you want to remove the religion?
This action cannot be reverted"; - $("#alert").dialog({resizable: false, title: "Remove religion", - buttons: { - Remove: function() { - relig.select("#religion"+religion).remove(); - relig.select("#religion-gap"+religion).remove(); - debug.select("#religionsCenter"+religion).remove(); + const message = 'Are you sure you want to remove the religion?
This action cannot be reverted'; + const onConfirm = () => { + relig.select('#religion' + religion).remove(); + relig.select('#religion-gap' + religion).remove(); + debug.select('#religionsCenter' + religion).remove(); - pack.cells.religion.forEach((r, i) => {if(r === religion) pack.cells.religion[i] = 0;}); - pack.religions[religion].removed = true; - const origin = pack.religions[religion].origin; - pack.religions.forEach(r => {if(r.origin === religion) r.origin = origin;}); - - refreshReligionsEditor(); - $(this).dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + pack.cells.religion.forEach((r, i) => { + if (r === religion) pack.cells.religion[i] = 0; + }); + pack.religions[religion].removed = true; + const origin = pack.religions[religion].origin; + pack.religions.forEach((r) => { + if (r.origin === religion) r.origin = origin; + }); + + refreshReligionsEditor(); + }; + confirmationDialog({title: 'Remove religion', message, confirm: 'Remove', onConfirm}); } function drawReligionCenters() { - debug.select("#religionCenters").remove(); - const religionCenters = debug.append("g").attr("id", "religionCenters") - .attr("stroke-width", 1.2).attr("stroke", "#444444").style("cursor", "move"); + debug.select('#religionCenters').remove(); + const religionCenters = debug.append('g').attr('id', 'religionCenters').attr('stroke-width', 1.2).attr('stroke', '#444444').style('cursor', 'move'); - const data = pack.religions.filter(r => r.i && r.center && r.cells && !r.removed); - religionCenters.selectAll("circle").data(data).enter().append("circle") - .attr("id", d => "religionsCenter"+d.i).attr("data-id", d => d.i) - .attr("r", 4).attr("fill", d => d.color) - .attr("cx", d => pack.cells.p[d.center][0]).attr("cy", d => pack.cells.p[d.center][1]) - .on("mouseenter", d => { - tip(d.name+ ". Drag to move the religion center", true); + const data = pack.religions.filter((r) => r.i && r.center && r.cells && !r.removed); + religionCenters + .selectAll('circle') + .data(data) + .enter() + .append('circle') + .attr('id', (d) => 'religionsCenter' + d.i) + .attr('data-id', (d) => d.i) + .attr('r', 4) + .attr('fill', (d) => d.color) + .attr('cx', (d) => pack.cells.p[d.center][0]) + .attr('cy', (d) => pack.cells.p[d.center][1]) + .on('mouseenter', (d) => { + tip(d.name + '. Drag to move the religion center', true); religionHighlightOn(event); - }).on("mouseleave", d => { + }) + .on('mouseleave', (d) => { tip('', true); religionHighlightOff(event); - }).call(d3.drag().on("start", religionCenterDrag)); + }) + .call(d3.drag().on('start', religionCenterDrag)); } function religionCenterDrag() { const el = d3.select(this); const r = +this.dataset.id; - d3.event.on("drag", () => { - el.attr("cx", d3.event.x).attr("cy", d3.event.y); + d3.event.on('drag', () => { + el.attr('cx', d3.event.x).attr('cy', d3.event.y); const cell = findCell(d3.event.x, d3.event.y); if (pack.cells.h[cell] < 20) return; // ignore dragging on water pack.religions[r].center = cell; @@ -347,23 +401,29 @@ function editReligions() { } function toggleLegend() { - if (legend.selectAll("*").size()) {clearLegend(); return;}; // hide legend - const data = pack.religions.filter(r => r.i && !r.removed && r.area).sort((a, b) => b.area - a.area).map(r => [r.i, r.color, r.name]); - drawLegend("Religions", data); + if (legend.selectAll('*').size()) { + clearLegend(); + return; + } // hide legend + const data = pack.religions + .filter((r) => r.i && !r.removed && r.area) + .sort((a, b) => b.area - a.area) + .map((r) => [r.i, r.color, r.name]); + drawLegend('Religions', data); } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; + if (body.dataset.type === 'absolute') { + body.dataset.type = 'percentage'; const totalArea = +religionsFooterArea.dataset.area; const totalPopulation = +religionsFooterPopulation.dataset.population; - body.querySelectorAll(":scope > div").forEach(function(el) { - el.querySelector(".biomeArea").innerHTML = rn(+el.dataset.area / totalArea * 100) + "%"; - el.querySelector(".culturePopulation").innerHTML = rn(+el.dataset.population / totalPopulation * 100) + "%"; + body.querySelectorAll(':scope > div').forEach(function (el) { + el.querySelector('.biomeArea').innerHTML = rn((+el.dataset.area / totalArea) * 100) + '%'; + el.querySelector('.culturePopulation').innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + '%'; }); } else { - body.dataset.type = "absolute"; + body.dataset.type = 'absolute'; religionsEditorAddLines(); } } @@ -371,12 +431,19 @@ function editReligions() { function showHierarchy() { // build hierarchy tree pack.religions[0].origin = null; - const religions = pack.religions.filter(r => !r.removed); - if (religions.length < 3) {tip("Not enough religions to show hierarchy", false, "error"); return;} - const root = d3.stratify().id(d => d.i).parentId(d => d.origin)(religions); + const religions = pack.religions.filter((r) => !r.removed); + if (religions.length < 3) { + tip('Not enough religions to show hierarchy', false, 'error'); + return; + } + const root = d3 + .stratify() + .id((d) => d.i) + .parentId((d) => d.origin)(religions); const treeWidth = root.leaves().length; const treeHeight = root.height; - const width = treeWidth * 40, height = treeHeight * 60; + const width = treeWidth * 40, + height = treeHeight * 60; const margin = {top: 10, right: 10, bottom: -5, left: 10}; const w = width - margin.left - margin.right; @@ -385,110 +452,151 @@ function editReligions() { // prepare svg alertMessage.innerHTML = "
"; - const svg = d3.select("#alertMessage").insert("svg", "#religionInfo").attr("id", "hierarchy") - .attr("width", width).attr("height", height).style("text-anchor", "middle"); - const graph = svg.append("g").attr("transform", `translate(10, -45)`); - const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa"); - const nodes = graph.append("g"); + const svg = d3.select('#alertMessage').insert('svg', '#religionInfo').attr('id', 'hierarchy').attr('width', width).attr('height', height).style('text-anchor', 'middle'); + const graph = svg.append('g').attr('transform', `translate(10, -45)`); + const links = graph.append('g').attr('fill', 'none').attr('stroke', '#aaaaaa'); + const nodes = graph.append('g'); renderTree(); function renderTree() { treeLayout(root); - links.selectAll('path').data(root.links()).enter() - .append('path').attr("d", d => {return "M" + d.source.x + "," + d.source.y - + "C" + d.source.x + "," + (d.source.y * 3 + d.target.y) / 4 - + " " + d.target.x + "," + (d.source.y * 2 + d.target.y) / 3 - + " " + d.target.x + "," + d.target.y;}); + links + .selectAll('path') + .data(root.links()) + .enter() + .append('path') + .attr('d', (d) => { + return ( + 'M' + + d.source.x + + ',' + + d.source.y + + 'C' + + d.source.x + + ',' + + (d.source.y * 3 + d.target.y) / 4 + + ' ' + + d.target.x + + ',' + + (d.source.y * 2 + d.target.y) / 3 + + ' ' + + d.target.x + + ',' + + d.target.y + ); + }); - const node = nodes.selectAll('g').data(root.descendants()).enter() - .append('g').attr("data-id", d => d.data.i).attr("stroke", "#333333") - .attr("transform", d => `translate(${d.x}, ${d.y})`) - .on("mouseenter", () => religionHighlightOn(event)) - .on("mouseleave", () => religionHighlightOff(event)) - .call(d3.drag().on("start", d => dragToReorigin(d))); + const node = nodes + .selectAll('g') + .data(root.descendants()) + .enter() + .append('g') + .attr('data-id', (d) => d.data.i) + .attr('stroke', '#333333') + .attr('transform', (d) => `translate(${d.x}, ${d.y})`) + .on('mouseenter', () => religionHighlightOn(event)) + .on('mouseleave', () => religionHighlightOff(event)) + .call(d3.drag().on('start', (d) => dragToReorigin(d))); - node.append("path").attr("d", d => { - if (d.data.type === "Folk") return "M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0"; else // circle - if (d.data.type === "Heresy") return "M0,-14L14,0L0,14L-14,0Z"; else // diamond - if (d.data.type === "Cult") return "M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z"; else // hex - if (!d.data.i) return "M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0"; else // small circle - return "M-11,-11h22v22h-22Z"; // square - }).attr("fill", d => d.data.i ? d.data.color : "#ffffff") - .attr("stroke-dasharray", d => d.data.cells ? "null" : "1"); + node + .append('path') + .attr('d', (d) => { + if (d.data.type === 'Folk') return 'M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0'; + // circle + else if (d.data.type === 'Heresy') return 'M0,-14L14,0L0,14L-14,0Z'; + // diamond + else if (d.data.type === 'Cult') return 'M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z'; + // hex + else if (!d.data.i) return 'M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0'; + // small circle + else return 'M-11,-11h22v22h-22Z'; // square + }) + .attr('fill', (d) => (d.data.i ? d.data.color : '#ffffff')) + .attr('stroke-dasharray', (d) => (d.data.cells ? 'null' : '1')); - node.append("text").attr("dy", ".35em").text(d => d.data.i ? d.data.code : ''); + node + .append('text') + .attr('dy', '.35em') + .text((d) => (d.data.i ? d.data.code : '')); } - $("#alert").dialog({ - title: "Religions tree", width: fitContent(), resizable: false, - position: {my: "left center", at: "left+10 center", of: "svg"}, buttons: {}, - close: () => {alertMessage.innerHTML = "";} + $('#alert').dialog({ + title: 'Religions tree', + width: fitContent(), + resizable: false, + position: {my: 'left center', at: 'left+10 center', of: 'svg'}, + buttons: {}, + close: () => { + alertMessage.innerHTML = ''; + } }); function dragToReorigin(d) { - if (isCtrlClick(d3.event.sourceEvent)) {changeCode(d); return;} + if (isCtrlClick(d3.event.sourceEvent)) { + changeCode(d); + return; + } - const originLine = graph.append("path") - .attr("class", "dragLine").attr("d", `M${d.x},${d.y}L${d.x},${d.y}`); + const originLine = graph.append('path').attr('class', 'dragLine').attr('d', `M${d.x},${d.y}L${d.x},${d.y}`); - d3.event.on("drag", () => { - originLine.attr("d", `M${d.x},${d.y}L${d3.event.x},${d3.event.y}`) + d3.event.on('drag', () => { + originLine.attr('d', `M${d.x},${d.y}L${d3.event.x},${d3.event.y}`); }); - d3.event.on("end", () => { + d3.event.on('end', () => { originLine.remove(); - const selected = graph.select("path.selected"); + const selected = graph.select('path.selected'); if (!selected.size()) return; const religion = d.data.i; const oldOrigin = d.data.origin; let newOrigin = selected.datum().data.i; if (newOrigin == oldOrigin) return; // already a child of the selected node if (newOrigin == religion) newOrigin = 0; // move to top - if (newOrigin && d.descendants().some(node => node.id == newOrigin)) return; // cannot be a child of its own child + if (newOrigin && d.descendants().some((node) => node.id == newOrigin)) return; // cannot be a child of its own child pack.religions[religion].origin = d.data.origin = newOrigin; // change data - showHierarchy() // update hierarchy + showHierarchy(); // update hierarchy }); } function changeCode(d) { - prompt(`Please provide an abbreviation for ${d.data.name}`, {default:d.data.code}, v => { + prompt(`Please provide an abbreviation for ${d.data.name}`, {default: d.data.code}, (v) => { pack.religions[d.data.i].code = v; - nodes.select("g[data-id='"+d.data.i+"']").select("text").text(v); + nodes + .select("g[data-id='" + d.data.i + "']") + .select('text') + .text(v); }); } } function toggleExtinct() { - body.dataset.extinct = body.dataset.extinct !== "show" ? "show" : "hide"; + body.dataset.extinct = body.dataset.extinct !== 'show' ? 'show' : 'hide'; religionsEditorAddLines(); } function enterReligionsManualAssignent() { - if (!layerIsOn("toggleReligions")) toggleReligions(); + if (!layerIsOn('toggleReligions')) toggleReligions(); customization = 7; - relig.append("g").attr("id", "temp"); - document.querySelectorAll("#religionsBottom > button").forEach(el => el.style.display = "none"); - document.getElementById("religionsManuallyButtons").style.display = "inline-block"; - debug.select("#religionCenters").style("display", "none"); + relig.append('g').attr('id', 'temp'); + document.querySelectorAll('#religionsBottom > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('religionsManuallyButtons').style.display = 'inline-block'; + debug.select('#religionCenters').style('display', 'none'); - religionsEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); - religionsFooter.style.display = "none"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); - $("#religionsEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}}); + religionsEditor.querySelectorAll('.hide').forEach((el) => el.classList.add('hidden')); + religionsFooter.style.display = 'none'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); + $('#religionsEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg'}}); - tip("Click on religion to select, drag the circle to change religion", true); - viewbox.style("cursor", "crosshair") - .on("click", selectReligionOnMapClick) - .call(d3.drag().on("start", dragReligionBrush)) - .on("touchmove mousemove", moveReligionBrush); + tip('Click on religion to select, drag the circle to change religion', true); + viewbox.style('cursor', 'crosshair').on('click', selectReligionOnMapClick).call(d3.drag().on('start', dragReligionBrush)).on('touchmove mousemove', moveReligionBrush); - body.querySelector("div").classList.add("selected"); + body.querySelector('div').classList.add('selected'); } function selectReligionOnLineClick(i) { if (customization !== 7) return; - body.querySelector("div.selected").classList.remove("selected"); - this.classList.add("selected"); + body.querySelector('div.selected').classList.remove('selected'); + this.classList.add('selected'); } function selectReligionOnMapClick() { @@ -496,21 +604,21 @@ function editReligions() { const i = findCell(point[0], point[1]); if (pack.cells.h[i] < 20) return; - const assigned = relig.select("#temp").select("polygon[data-cell='"+i+"']"); - const religion = assigned.size() ? +assigned.attr("data-religion") : pack.cells.religion[i]; + const assigned = relig.select('#temp').select("polygon[data-cell='" + i + "']"); + const religion = assigned.size() ? +assigned.attr('data-religion') : pack.cells.religion[i]; - body.querySelector("div.selected").classList.remove("selected"); - body.querySelector("div[data-id='"+religion+"']").classList.add("selected"); + body.querySelector('div.selected').classList.remove('selected'); + body.querySelector("div[data-id='" + religion + "']").classList.add('selected'); } - + function dragReligionBrush() { const r = +religionsManuallyBrushNumber.value; - d3.event.on("drag", () => { + d3.event.on('drag', () => { if (!d3.event.dx && !d3.event.dy) return; const p = d3.mouse(this); moveCircle(p[0], p[1], r); - + const found = r > 5 ? findAll(p[0], p[1], r) : [findCell(p[0], p[1], r)]; const selection = found.filter(isLand); if (selection) changeReligionForSelection(selection); @@ -519,19 +627,19 @@ function editReligions() { // change religion within selection function changeReligionForSelection(selection) { - const temp = relig.select("#temp"); - const selected = body.querySelector("div.selected"); + const temp = relig.select('#temp'); + const selected = body.querySelector('div.selected'); const r = +selected.dataset.id; // religionNew - const color = pack.religions[r].color || "#ffffff"; + const color = pack.religions[r].color || '#ffffff'; - selection.forEach(function(i) { - const exists = temp.select("polygon[data-cell='"+i+"']"); - const religionOld = exists.size() ? +exists.attr("data-religion") : pack.cells.religion[i]; + selection.forEach(function (i) { + const exists = temp.select("polygon[data-cell='" + i + "']"); + const religionOld = exists.size() ? +exists.attr('data-religion') : pack.cells.religion[i]; if (r === religionOld) return; // change of append new element - if (exists.size()) exists.attr("data-religion", r).attr("fill", color); - else temp.append("polygon").attr("data-cell", i).attr("data-religion", r).attr("points", getPackPolygon(i)).attr("fill", color); + if (exists.size()) exists.attr('data-religion', r).attr('fill', color); + else temp.append('polygon').attr('data-cell', i).attr('data-religion', r).attr('points', getPackPolygon(i)).attr('fill', color); }); } @@ -543,8 +651,8 @@ function editReligions() { } function applyReligionsManualAssignent() { - const changed = relig.select("#temp").selectAll("polygon"); - changed.each(function() { + const changed = relig.select('#temp').selectAll('polygon'); + changed.each(function () { const i = +this.dataset.cell; const r = +this.dataset.religion; pack.cells.religion[i] = r; @@ -557,49 +665,58 @@ function editReligions() { } exitReligionsManualAssignment(); } - + function exitReligionsManualAssignment(close) { customization = 0; - relig.select("#temp").remove(); + relig.select('#temp').remove(); removeCircle(); - document.querySelectorAll("#religionsBottom > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("religionsManuallyButtons").style.display = "none"; + document.querySelectorAll('#religionsBottom > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('religionsManuallyButtons').style.display = 'none'; - religionsEditor.querySelectorAll(".hide").forEach(el => el.classList.remove("hidden")); - religionsFooter.style.display = "block"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if(!close) $("#religionsEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}}); + religionsEditor.querySelectorAll('.hide').forEach((el) => el.classList.remove('hidden')); + religionsFooter.style.display = 'block'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (!close) $('#religionsEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg'}}); - debug.select("#religionCenters").style("display", null); + debug.select('#religionCenters').style('display', null); restoreDefaultEvents(); clearMainTip(); - const selected = body.querySelector("div.selected"); - if (selected) selected.classList.remove("selected"); + const selected = body.querySelector('div.selected'); + if (selected) selected.classList.remove('selected'); } function enterAddReligionMode() { - if (this.classList.contains("pressed")) {exitAddReligionMode(); return;}; + if (this.classList.contains('pressed')) { + exitAddReligionMode(); + return; + } customization = 8; - this.classList.add("pressed"); - tip("Click on the map to add a new religion", true); - viewbox.style("cursor", "crosshair").on("click", addReligion); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); + this.classList.add('pressed'); + tip('Click on the map to add a new religion', true); + viewbox.style('cursor', 'crosshair').on('click', addReligion); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); } function exitAddReligionMode() { customization = 0; restoreDefaultEvents(); clearMainTip(); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if (religionsAdd.classList.contains("pressed")) religionsAdd.classList.remove("pressed"); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (religionsAdd.classList.contains('pressed')) religionsAdd.classList.remove('pressed'); } function addReligion() { const point = d3.mouse(this); const center = findCell(point[0], point[1]); - if (pack.cells.h[center] < 20) {tip("You cannot place religion center into the water. Please click on a land cell", false, "error"); return;} - const occupied = pack.religions.some(r => !r.removed && r.center === center); - if (occupied) {tip("This cell is already a religion center. Please select a different cell", false, "error"); return;} + if (pack.cells.h[center] < 20) { + tip('You cannot place religion center into the water. Please click on a land cell', false, 'error'); + return; + } + const occupied = pack.religions.some((r) => !r.removed && r.center === center); + if (occupied) { + tip('This cell is already a religion center. Please select a different cell', false, 'error'); + return; + } if (d3.event.shiftKey === false) exitAddReligionMode(); Religions.add(center); @@ -610,28 +727,27 @@ function editReligions() { } function downloadReligionsData() { - const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value; - let data = "Id,Religion,Color,Type,Form,Deity,Area "+unit+",Believers\n"; // headers + const unit = areaUnit.value === 'square' ? distanceUnitInput.value + '2' : areaUnit.value; + let data = 'Id,Religion,Color,Type,Form,Deity,Area ' + unit + ',Believers\n'; // headers - body.querySelectorAll(":scope > div").forEach(function(el) { - data += el.dataset.id + ","; - data += el.dataset.name + ","; - data += el.dataset.color + ","; - data += el.dataset.type + ","; - data += el.dataset.form + ","; - data += el.dataset.deity.replace(",", " -") + ","; - data += el.dataset.area + ","; - data += el.dataset.population + "\n"; + body.querySelectorAll(':scope > div').forEach(function (el) { + data += el.dataset.id + ','; + data += el.dataset.name + ','; + data += el.dataset.color + ','; + data += el.dataset.type + ','; + data += el.dataset.form + ','; + data += el.dataset.deity.replace(',', ' -') + ','; + data += el.dataset.area + ','; + data += el.dataset.population + '\n'; }); - const name = getFileName("Religions") + ".csv"; + const name = getFileName('Religions') + '.csv'; downloadFile(data, name); } - + function closeReligionsEditor() { - debug.select("#religionCenters").remove(); - exitReligionsManualAssignment("close"); + debug.select('#religionCenters').remove(); + exitReligionsManualAssignment('close'); exitAddReligionMode(); } - -} \ No newline at end of file +} diff --git a/modules/ui/resources-editor.js b/modules/ui/resources-editor.js index 36b8ea8c..3048856a 100644 --- a/modules/ui/resources-editor.js +++ b/modules/ui/resources-editor.js @@ -321,8 +321,7 @@ function editResources() { function regenerateCurrentResources() { const message = 'Are you sure you want to regenerate resources?
This action cannot be reverted'; - const onConfirm = () => regenerateResources(); - confirmationDialog({title: 'Regenerate resources', message, confirm: 'Regenerate', onConfirm}); + confirmationDialog({title: 'Regenerate resources', message, confirm: 'Regenerate', onConfirm: regenerateResources}); } function resourcesRestoreDefaults() { diff --git a/modules/ui/rivers-editor.js b/modules/ui/rivers-editor.js index bc8181ee..5eb1de5d 100644 --- a/modules/ui/rivers-editor.js +++ b/modules/ui/rivers-editor.js @@ -1,20 +1,21 @@ -"use strict"; +'use strict'; function editRiver(id) { if (customization) return; - if (elSelected && d3.event && d3.event.target.id === elSelected.attr("id")) return; - closeDialogs(".stable"); - if (!layerIsOn("toggleRivers")) toggleRivers(); + if (elSelected && d3.event && d3.event.target.id === elSelected.attr('id')) return; + closeDialogs('.stable'); + if (!layerIsOn('toggleRivers')) toggleRivers(); const node = id ? document.getElementById(id) : d3.event.target; - elSelected = d3.select(node).on("click", addInterimControlPoint); - viewbox.on("touchmove mousemove", showEditorTips); - debug.append("g").attr("id", "controlPoints").attr("transform", elSelected.attr("transform")); + elSelected = d3.select(node).on('click', addInterimControlPoint); + viewbox.on('touchmove mousemove', showEditorTips); + debug.append('g').attr('id', 'controlPoints').attr('transform', elSelected.attr('transform')); updateRiverData(); drawControlPoints(node); - $("#riverEditor").dialog({ - title: "Edit River", resizable: false, - position: {my: "center top+80", at: "top", of: node, collision: "fit"}, + $('#riverEditor').dialog({ + title: 'Edit River', + resizable: false, + position: {my: 'center top+80', at: 'top', of: node, collision: 'fit'}, close: closeRiverEditor }); @@ -22,64 +23,64 @@ function editRiver(id) { modules.editRiver = true; // add listeners - document.getElementById("riverName").addEventListener("input", changeName); - document.getElementById("riverType").addEventListener("input", changeType); - document.getElementById("riverNameCulture").addEventListener("click", generateNameCulture); - document.getElementById("riverNameRandom").addEventListener("click", generateNameRandom); - document.getElementById("riverMainstem").addEventListener("change", changeParent); + document.getElementById('riverName').addEventListener('input', changeName); + document.getElementById('riverType').addEventListener('input', changeType); + document.getElementById('riverNameCulture').addEventListener('click', generateNameCulture); + document.getElementById('riverNameRandom').addEventListener('click', generateNameRandom); + document.getElementById('riverMainstem').addEventListener('change', changeParent); - document.getElementById("riverSourceWidth").addEventListener("input", changeSourceWidth); - document.getElementById("riverWidthFactor").addEventListener("input", changeWidthFactor); + document.getElementById('riverSourceWidth').addEventListener('input', changeSourceWidth); + document.getElementById('riverWidthFactor').addEventListener('input', changeWidthFactor); - document.getElementById("riverNew").addEventListener("click", toggleRiverCreationMode); - document.getElementById("riverEditStyle").addEventListener("click", () => editStyle("rivers")); - document.getElementById("riverElevationProfile").addEventListener("click", showElevationProfile); - document.getElementById("riverLegend").addEventListener("click", editRiverLegend); - document.getElementById("riverRemove").addEventListener("click", removeRiver); + document.getElementById('riverNew').addEventListener('click', toggleRiverCreationMode); + document.getElementById('riverEditStyle').addEventListener('click', () => editStyle('rivers')); + document.getElementById('riverElevationProfile').addEventListener('click', showElevationProfile); + document.getElementById('riverLegend').addEventListener('click', editRiverLegend); + document.getElementById('riverRemove').addEventListener('click', removeRiver); function showEditorTips() { showMainTip(); - if (d3.event.target.parentNode.id === elSelected.attr("id")) tip("Drag to move, click to add a control point"); else - if (d3.event.target.parentNode.id === "controlPoints") tip("Drag to move, click to delete the control point"); + if (d3.event.target.parentNode.id === elSelected.attr('id')) tip('Drag to move, click to add a control point'); + else if (d3.event.target.parentNode.id === 'controlPoints') tip('Drag to move, click to delete the control point'); } function getRiver() { - const riverId = +elSelected.attr("id").slice(5); - const river = pack.rivers.find(r => r.i === riverId); + const riverId = +elSelected.attr('id').slice(5); + const river = pack.rivers.find((r) => r.i === riverId); return river; } function updateRiverData() { const r = getRiver(); - document.getElementById("riverName").value = r.name; - document.getElementById("riverType").value = r.type; + document.getElementById('riverName').value = r.name; + document.getElementById('riverType').value = r.type; - const parentSelect = document.getElementById("riverMainstem"); + const parentSelect = document.getElementById('riverMainstem'); parentSelect.options.length = 0; const parent = r.parent || r.i; - const sortedRivers = pack.rivers.slice().sort((a, b) => a.name > b.name ? 1 : -1); - sortedRivers.forEach(river => { + const sortedRivers = pack.rivers.slice().sort((a, b) => (a.name > b.name ? 1 : -1)); + sortedRivers.forEach((river) => { const opt = new Option(river.name, river.i, false, river.i === parent); parentSelect.options.add(opt); }); - document.getElementById("riverBasin").value = pack.rivers.find(river => river.i === r.basin).name; + document.getElementById('riverBasin').value = pack.rivers.find((river) => river.i === r.basin).name; - document.getElementById("riverDischarge").value = r.discharge + " m³/s"; + document.getElementById('riverDischarge').value = r.discharge + ' m³/s'; r.length = elSelected.node().getTotalLength() / 2; - const length = rn(r.length * distanceScaleInput.value) + " " + distanceUnitInput.value; - document.getElementById("riverLength").value = length; - const width = rn(r.width * distanceScaleInput.value, 3) + " " + distanceUnitInput.value; - document.getElementById("riverWidth").value = width; + const length = rn(r.length * distanceScaleInput.value) + ' ' + distanceUnitInput.value; + document.getElementById('riverLength').value = length; + const width = rn(r.width * distanceScaleInput.value, 3) + ' ' + distanceUnitInput.value; + document.getElementById('riverWidth').value = width; - document.getElementById("riverSourceWidth").value = r.sourceWidth; - document.getElementById("riverWidthFactor").value = r.widthFactor; + document.getElementById('riverSourceWidth').value = r.sourceWidth; + document.getElementById('riverWidthFactor').value = r.widthFactor; } function drawControlPoints(node) { const length = getRiver().length; const segments = Math.ceil(length / 4); - const increment = rn(length / segments * 1e5); + const increment = rn((length / segments) * 1e5); for (let i = increment * segments, c = i; i >= 0; i -= increment, c += increment) { const p1 = node.getPointAtLength(i / 1e5); const p2 = node.getPointAtLength(c / 1e5); @@ -88,37 +89,39 @@ function editRiver(id) { } function addControlPoint(point, before = null) { - debug.select("#controlPoints").insert("circle", before) - .attr("cx", point[0]).attr("cy", point[1]).attr("r", .6) - .call(d3.drag().on("drag", dragControlPoint)) - .on("click", clickControlPoint); + debug.select('#controlPoints').insert('circle', before).attr('cx', point[0]).attr('cy', point[1]).attr('r', 0.6).call(d3.drag().on('drag', dragControlPoint)).on('click', clickControlPoint); } function dragControlPoint() { - this.setAttribute("cx", d3.event.x); - this.setAttribute("cy", d3.event.y); + this.setAttribute('cx', d3.event.x); + this.setAttribute('cy', d3.event.y); redrawRiver(); } function redrawRiver() { const points = []; - debug.select("#controlPoints").selectAll("circle").each(function() { - points.push([+this.getAttribute("cx"), +this.getAttribute("cy")]); - }); + debug + .select('#controlPoints') + .selectAll('circle') + .each(function () { + points.push([+this.getAttribute('cx'), +this.getAttribute('cy')]); + }); if (points.length < 2) return; if (points.length === 2) { - const p0 = points[0], p1 = points[1]; + const p0 = points[0], + p1 = points[1]; const angle = Math.atan2(p1[1] - p0[1], p1[0] - p0[0]); - const sin = Math.sin(angle), cos = Math.cos(angle); - elSelected.attr("d", `M${p0[0]},${p0[1]} L${p1[0]},${p1[1]} l${-sin/2},${cos/2} Z`); + const sin = Math.sin(angle), + cos = Math.cos(angle); + elSelected.attr('d', `M${p0[0]},${p0[1]} L${p1[0]},${p1[1]} l${-sin / 2},${cos / 2} Z`); return; } - const widthFactor = +document.getElementById("riverWidthFactor").value; - const sourceWidth = +document.getElementById("riverSourceWidth").value; + const widthFactor = +document.getElementById('riverWidthFactor').value; + const sourceWidth = +document.getElementById('riverSourceWidth').value; const [path, length, offset] = Rivers.getPath(points, widthFactor, sourceWidth); - elSelected.attr("d", path); + elSelected.attr('d', path); const r = getRiver(); if (r) { @@ -137,10 +140,10 @@ function editRiver(id) { function addInterimControlPoint() { const point = d3.mouse(this); - const controls = document.getElementById("controlPoints").querySelectorAll("circle"); - const points = Array.from(controls).map(circle => [+circle.getAttribute("cx"), +circle.getAttribute("cy")]); + const controls = document.getElementById('controlPoints').querySelectorAll('circle'); + const points = Array.from(controls).map((circle) => [+circle.getAttribute('cx'), +circle.getAttribute('cy')]); const index = getSegmentId(points, point, 2); - addControlPoint(point, ":nth-child(" + (index+1) + ")"); + addControlPoint(point, ':nth-child(' + (index + 1) + ')'); redrawRiver(); } @@ -160,14 +163,14 @@ function editRiver(id) { function generateNameRandom() { const r = getRiver(); - if (r) r.name = riverName.value = Names.getBase(rand(nameBases.length-1)); + if (r) r.name = riverName.value = Names.getBase(rand(nameBases.length - 1)); } function changeParent() { const r = getRiver(); r.parent = +this.value; - r.basin = pack.rivers.find(river => river.i === r.parent).basin; - document.getElementById("riverBasin").value = pack.rivers.find(river => river.i === r.basin).name; + r.basin = pack.rivers.find((river) => river.i === r.parent).basin; + document.getElementById('riverBasin').value = pack.rivers.find((river) => river.i === r.basin).name; } function changeSourceWidth() { @@ -186,26 +189,26 @@ function editRiver(id) { } function editRiverLegend() { - const id = elSelected.attr("id"); + const id = elSelected.attr('id'); const river = getRiver(); - editNotes(id, river.name + " " + river.type); + editNotes(id, river.name + ' ' + river.type); } function toggleRiverCreationMode() { - if (document.getElementById("riverNew").classList.contains("pressed")) exitRiverCreationMode(); + if (document.getElementById('riverNew').classList.contains('pressed')) exitRiverCreationMode(); else { - document.getElementById("riverNew").classList.add("pressed"); - tip("Click on map to add control points", true, "warn"); - viewbox.on("click", addPointOnClick).style("cursor", "crosshair"); - elSelected.on("click", null); + document.getElementById('riverNew').classList.add('pressed'); + tip('Click on map to add control points', true, 'warn'); + viewbox.on('click', addPointOnClick).style('cursor', 'crosshair'); + elSelected.on('click', null); } } function addPointOnClick() { - if (!elSelected.attr("data-new")) { - debug.select("#controlPoints").selectAll("circle").remove(); - const id = getNextId("river"); - elSelected = d3.select(elSelected.node().parentNode).append("path").attr("id", id).attr("data-new", 1); + if (!elSelected.attr('data-new')) { + debug.select('#controlPoints').selectAll('circle').remove(); + const id = getNextId('river'); + elSelected = d3.select(elSelected.node().parentNode).append('path').attr('id', id).attr('data-new', 1); } // add control point @@ -215,20 +218,22 @@ function editRiver(id) { } function exitRiverCreationMode() { - riverNew.classList.remove("pressed"); + riverNew.classList.remove('pressed'); clearMainTip(); - viewbox.on("click", clicked).style("cursor", "default"); - elSelected.on("click", addInterimControlPoint); + viewbox.on('click', clicked).style('cursor', 'default'); + elSelected.on('click', addInterimControlPoint); - if (!elSelected.attr("data-new")) return; // no need to create a new river - elSelected.attr("data-new", null); + if (!elSelected.attr('data-new')) return; // no need to create a new river + elSelected.attr('data-new', null); // add a river - const r = +elSelected.attr("id").slice(5); - const node = elSelected.node(), length = node.getTotalLength() / 2; + const r = +elSelected.attr('id').slice(5); + const node = elSelected.node(), + length = node.getTotalLength() / 2; const cells = []; - const segments = Math.ceil(length / 4), increment = rn(length / segments * 1e5); + const segments = Math.ceil(length / 4), + increment = rn((length / segments) * 1e5); for (let i = increment * segments, c = i; i >= 0; i -= increment, c += increment) { const p = node.getPointAtLength(i / 1e5); const cell = findCell(p.x, p.y); @@ -236,38 +241,34 @@ function editRiver(id) { cells.push(cell); } - const source = cells[0], mouth = last(cells); + const source = cells[0], + mouth = last(cells); const name = Rivers.getName(mouth); - const smallLength = pack.rivers.map(r => r.length||0).sort((a,b) => a-b)[Math.ceil(pack.rivers.length * .15)]; - const type = length < smallLength ? rw({"Creek":9, "River":3, "Brook":3, "Stream":1}) : "River"; + const smallLength = pack.rivers.map((r) => r.length || 0).sort((a, b) => a - b)[Math.ceil(pack.rivers.length * 0.15)]; + const type = length < smallLength ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : 'River'; const discharge = rn(cells.length * 20 * Math.random()); - const widthFactor = +document.getElementById("riverWidthFactor").value; - const sourceWidth = +document.getElementById("riverSourceWidth").value; + const widthFactor = +document.getElementById('riverWidthFactor').value; + const sourceWidth = +document.getElementById('riverSourceWidth').value; - pack.rivers.push({i:r, source, mouth, discharge, length, width: sourceWidth, widthFactor, sourceWidth, parent:0, name, type, basin:r}); + pack.rivers.push({i: r, source, mouth, discharge, length, width: sourceWidth, widthFactor, sourceWidth, parent: 0, name, type, basin: r}); } function removeRiver() { - alertMessage.innerHTML = "Are you sure you want to remove the river? All tributaries will be auto-removed"; - $("#alert").dialog({resizable: false, width: "22em", title: "Remove river", - buttons: { - Remove: function() { - $(this).dialog("close"); - const river = +elSelected.attr("id").slice(5); - Rivers.remove(river); - elSelected.remove(); // keep if river if missed in pack.rivers - $("#riverEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the river?
All tributaries will be auto-removed'; + const onConfirm = () => { + const river = +elSelected.attr('id').slice(5); + Rivers.remove(river); + elSelected.remove(); // if river if missed in pack.rivers + $('#riverEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove river', message, confirm: 'Remove', onConfirm}); } function closeRiverEditor() { exitRiverCreationMode(); - elSelected.on("click", null); - debug.select("#controlPoints").remove(); + elSelected.on('click', null); + debug.select('#controlPoints').remove(); unselect(); } } diff --git a/modules/ui/rivers-overview.js b/modules/ui/rivers-overview.js index f96e9857..3ad9d13b 100644 --- a/modules/ui/rivers-overview.js +++ b/modules/ui/rivers-overview.js @@ -1,39 +1,41 @@ -"use strict"; +'use strict'; function overviewRivers() { if (customization) return; - closeDialogs("#riversOverview, .stable"); - if (!layerIsOn("toggleRivers")) toggleRivers(); + closeDialogs('#riversOverview, .stable'); + if (!layerIsOn('toggleRivers')) toggleRivers(); - const body = document.getElementById("riversBody"); + const body = document.getElementById('riversBody'); riversOverviewAddLines(); - $("#riversOverview").dialog(); + $('#riversOverview').dialog(); if (modules.overviewRivers) return; modules.overviewRivers = true; - $("#riversOverview").dialog({ - title: "Rivers Overview", resizable: false, width: fitContent(), - position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} + $('#riversOverview').dialog({ + title: 'Rivers Overview', + resizable: false, + width: fitContent(), + position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'} }); // add listeners - document.getElementById("riversOverviewRefresh").addEventListener("click", riversOverviewAddLines); - document.getElementById("addNewRiver").addEventListener("click", toggleAddRiver); - document.getElementById("riversBasinHighlight").addEventListener("click", toggleBasinsHightlight); - document.getElementById("riversExport").addEventListener("click", downloadRiversData); - document.getElementById("riversRemoveAll").addEventListener("click", triggerAllRiversRemove); + document.getElementById('riversOverviewRefresh').addEventListener('click', riversOverviewAddLines); + document.getElementById('addNewRiver').addEventListener('click', toggleAddRiver); + document.getElementById('riversBasinHighlight').addEventListener('click', toggleBasinsHightlight); + document.getElementById('riversExport').addEventListener('click', downloadRiversData); + document.getElementById('riversRemoveAll').addEventListener('click', triggerAllRiversRemove); // add line for each river function riversOverviewAddLines() { - body.innerHTML = ""; - let lines = ""; + body.innerHTML = ''; + let lines = ''; const unit = distanceUnitInput.value; for (const r of pack.rivers) { - const discharge = r.discharge + " m³/s"; - const length = rn(r.length * distanceScaleInput.value) + " " + unit; - const width = rn(r.width * distanceScaleInput.value, 3) + " " + unit; - const basin = pack.rivers.find(river => river.i === r.basin)?.name; + const discharge = r.discharge + ' m³/s'; + const length = rn(r.length * distanceScaleInput.value) + ' ' + unit; + const width = rn(r.width * distanceScaleInput.value, 3) + ' ' + unit; + const basin = pack.rivers.find((river) => river.i === r.basin)?.name; lines += `
@@ -47,115 +49,107 @@ function overviewRivers() {
`; } - body.insertAdjacentHTML("beforeend", lines); + body.insertAdjacentHTML('beforeend', lines); // update footer riversFooterNumber.innerHTML = pack.rivers.length; - const averageDischarge = rn(d3.mean(pack.rivers.map(r => r.discharge))); - riversFooterDischarge.innerHTML = averageDischarge + " m³/s"; - const averageLength = rn(d3.mean(pack.rivers.map(r => r.length)) ); - riversFooterLength.innerHTML = (averageLength * distanceScaleInput.value) + " " + unit; - const averageWidth = rn(d3.mean(pack.rivers.map(r => r.width)), 3); - riversFooterWidth.innerHTML = rn(averageWidth * distanceScaleInput.value, 3) + " " + unit; + const averageDischarge = rn(d3.mean(pack.rivers.map((r) => r.discharge))); + riversFooterDischarge.innerHTML = averageDischarge + ' m³/s'; + const averageLength = rn(d3.mean(pack.rivers.map((r) => r.length))); + riversFooterLength.innerHTML = averageLength * distanceScaleInput.value + ' ' + unit; + const averageWidth = rn(d3.mean(pack.rivers.map((r) => r.width)), 3); + riversFooterWidth.innerHTML = rn(averageWidth * distanceScaleInput.value, 3) + ' ' + unit; // add listeners - body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => riverHighlightOn(ev))); - body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => riverHighlightOff(ev))); - body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomToRiver)); - body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openRiverEditor)); - body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", triggerRiverRemove)); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('mouseenter', (ev) => riverHighlightOn(ev))); + body.querySelectorAll('div.states').forEach((el) => el.addEventListener('mouseleave', (ev) => riverHighlightOff(ev))); + body.querySelectorAll('div > span.icon-dot-circled').forEach((el) => el.addEventListener('click', zoomToRiver)); + body.querySelectorAll('div > span.icon-pencil').forEach((el) => el.addEventListener('click', openRiverEditor)); + body.querySelectorAll('div > span.icon-trash-empty').forEach((el) => el.addEventListener('click', triggerRiverRemove)); applySorting(riversHeader); } function riverHighlightOn(event) { - if (!layerIsOn("toggleRivers")) toggleRivers(); + if (!layerIsOn('toggleRivers')) toggleRivers(); const r = +event.target.dataset.id; - rivers.select("#river"+r).attr("stroke", "red").attr("stroke-width", 1); + rivers + .select('#river' + r) + .attr('stroke', 'red') + .attr('stroke-width', 1); } function riverHighlightOff(e) { const r = +e.target.dataset.id; - rivers.select("#river"+r).attr("stroke", null).attr("stroke-width", null); + rivers + .select('#river' + r) + .attr('stroke', null) + .attr('stroke-width', null); } function zoomToRiver() { const r = +this.parentNode.dataset.id; - const river = rivers.select("#river"+r).node(); + const river = rivers.select('#river' + r).node(); highlightElement(river); } function toggleBasinsHightlight() { - if (rivers.attr("data-basin") === "hightlighted") { - rivers.selectAll("*").attr("fill", null); - rivers.attr("data-basin", null); + if (rivers.attr('data-basin') === 'hightlighted') { + rivers.selectAll('*').attr('fill', null); + rivers.attr('data-basin', null); } else { - rivers.attr("data-basin", "hightlighted"); - const basins = [...new Set(pack.rivers.map(r => r.basin))]; - const colors = ["#1f77b4","#ff7f0e","#2ca02c","#d62728","#9467bd","#8c564b","#e377c2","#7f7f7f","#bcbd22","#17becf"]; + rivers.attr('data-basin', 'hightlighted'); + const basins = [...new Set(pack.rivers.map((r) => r.basin))]; + const colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf']; basins.forEach((b, i) => { const color = colors[i % colors.length]; - pack.rivers.filter(r => r.basin === b).forEach(r => { - rivers.select("#river"+r.i).attr("fill", color); - }); + pack.rivers + .filter((r) => r.basin === b) + .forEach((r) => { + rivers.select('#river' + r.i).attr('fill', color); + }); }); } } function downloadRiversData() { - let data = "Id,River,Type,Discharge,Length,Width,Basin\n"; // headers + let data = 'Id,River,Type,Discharge,Length,Width,Basin\n'; // headers - body.querySelectorAll(":scope > div").forEach(function(el) { + body.querySelectorAll(':scope > div').forEach(function (el) { const d = el.dataset; - const discharge = d.discharge + " m³/s" - const length = rn(d.length * distanceScaleInput.value) + " " + distanceUnitInput.value; - const width = rn(d.width * distanceScaleInput.value, 3) + " " + distanceUnitInput.value; - data += [d.id, d.name, d.type, discharge, length, width, d.basin].join(",") + "\n"; + const discharge = d.discharge + ' m³/s'; + const length = rn(d.length * distanceScaleInput.value) + ' ' + distanceUnitInput.value; + const width = rn(d.width * distanceScaleInput.value, 3) + ' ' + distanceUnitInput.value; + data += [d.id, d.name, d.type, discharge, length, width, d.basin].join(',') + '\n'; }); - const name = getFileName("Rivers") + ".csv"; + const name = getFileName('Rivers') + '.csv'; downloadFile(data, name); } function openRiverEditor() { - editRiver("river"+this.parentNode.dataset.id); + editRiver('river' + this.parentNode.dataset.id); } function triggerRiverRemove() { const river = +this.parentNode.dataset.id; - alertMessage.innerHTML = `Are you sure you want to remove the river? - All tributaries will be auto-removed`; - $("#alert").dialog({resizable: false, width: "22em", title: "Remove river", - buttons: { - Remove: function() { - Rivers.remove(river); - riversOverviewAddLines(); - $(this).dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the river?
All tributaries will be auto-removed'; + const onConfirm = () => { + Rivers.remove(river); + riversOverviewAddLines(); + }; + confirmationDialog({title: 'Remove river', message, confirm: 'Remove', onConfirm}); } function triggerAllRiversRemove() { - alertMessage.innerHTML = `Are you sure you want to remove all rivers?`; - $("#alert").dialog({resizable: false, title: "Remove all rivers", - buttons: { - Remove: function() { - $(this).dialog("close"); - removeAllRivers(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove all rivers?
This action cannot be reverted'; + const onConfirm = () => { + pack.rivers = []; + rivers.selectAll('*').remove(); + riversOverviewAddLines(); + }; + confirmationDialog({title: 'Remove all rivers', message, confirm: 'Remove', onConfirm}); } - - function removeAllRivers() { - pack.rivers = []; - rivers.selectAll("*").remove(); - riversOverviewAddLines(); - } - } diff --git a/modules/ui/routes-editor.js b/modules/ui/routes-editor.js index 0b743652..e0ccd106 100644 --- a/modules/ui/routes-editor.js +++ b/modules/ui/routes-editor.js @@ -1,93 +1,94 @@ -"use strict"; +'use strict'; function editRoute(onClick) { if (customization) return; - if (!onClick && elSelected && d3.event.target.id === elSelected.attr("id")) return; - closeDialogs(".stable"); - if (!layerIsOn("toggleRoutes")) toggleRoutes(); + if (!onClick && elSelected && d3.event.target.id === elSelected.attr('id')) return; + closeDialogs('.stable'); + if (!layerIsOn('toggleRoutes')) toggleRoutes(); - $("#routeEditor").dialog({ - title: "Edit Route", resizable: false, - position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"}, + $('#routeEditor').dialog({ + title: 'Edit Route', + resizable: false, + position: {my: 'center top+60', at: 'top', of: d3.event, collision: 'fit'}, close: closeRoutesEditor }); - debug.append("g").attr("id", "controlPoints"); + debug.append('g').attr('id', 'controlPoints'); const node = onClick ? elSelected.node() : d3.event.target; - elSelected = d3.select(node).on("click", addInterimControlPoint); + elSelected = d3.select(node).on('click', addInterimControlPoint); drawControlPoints(node); selectRouteGroup(node); - viewbox.on("touchmove mousemove", showEditorTips); + viewbox.on('touchmove mousemove', showEditorTips); if (onClick) toggleRouteCreationMode(); if (modules.editRoute) return; modules.editRoute = true; // add listeners - document.getElementById("routeGroupsShow").addEventListener("click", showGroupSection); - document.getElementById("routeGroup").addEventListener("change", changeRouteGroup); - document.getElementById("routeGroupAdd").addEventListener("click", toggleNewGroupInput); - document.getElementById("routeGroupName").addEventListener("change", createNewGroup); - document.getElementById("routeGroupRemove").addEventListener("click", removeRouteGroup); - document.getElementById("routeGroupsHide").addEventListener("click", hideGroupSection); - document.getElementById("routeElevationProfile").addEventListener("click", showElevationProfile); + document.getElementById('routeGroupsShow').addEventListener('click', showGroupSection); + document.getElementById('routeGroup').addEventListener('change', changeRouteGroup); + document.getElementById('routeGroupAdd').addEventListener('click', toggleNewGroupInput); + document.getElementById('routeGroupName').addEventListener('change', createNewGroup); + document.getElementById('routeGroupRemove').addEventListener('click', removeRouteGroup); + document.getElementById('routeGroupsHide').addEventListener('click', hideGroupSection); + document.getElementById('routeElevationProfile').addEventListener('click', showElevationProfile); - document.getElementById("routeEditStyle").addEventListener("click", editGroupStyle); - document.getElementById("routeSplit").addEventListener("click", toggleRouteSplitMode); - document.getElementById("routeLegend").addEventListener("click", editRouteLegend); - document.getElementById("routeNew").addEventListener("click", toggleRouteCreationMode); - document.getElementById("routeRemove").addEventListener("click", removeRoute); + document.getElementById('routeEditStyle').addEventListener('click', editGroupStyle); + document.getElementById('routeSplit').addEventListener('click', toggleRouteSplitMode); + document.getElementById('routeLegend').addEventListener('click', editRouteLegend); + document.getElementById('routeNew').addEventListener('click', toggleRouteCreationMode); + document.getElementById('routeRemove').addEventListener('click', removeRoute); function showEditorTips() { showMainTip(); - if (routeNew.classList.contains("pressed")) return; - if (d3.event.target.id === elSelected.attr("id")) tip("Click to add a control point"); else - if (d3.event.target.parentNode.id === "controlPoints") tip("Drag to move, click to delete the control point"); + if (routeNew.classList.contains('pressed')) return; + if (d3.event.target.id === elSelected.attr('id')) tip('Click to add a control point'); + else if (d3.event.target.parentNode.id === 'controlPoints') tip('Drag to move, click to delete the control point'); } function drawControlPoints(node) { const l = node.getTotalLength(); const increment = l / Math.ceil(l / 4); - for (let i=0; i <= l; i += increment) { + for (let i = 0; i <= l; i += increment) { const point = node.getPointAtLength(i); addControlPoint([point.x, point.y]); } - routeLength.innerHTML = rn(l * distanceScaleInput.value) + " " + distanceUnitInput.value; + routeLength.innerHTML = rn(l * distanceScaleInput.value) + ' ' + distanceUnitInput.value; } - + function addControlPoint(point, before = null) { - debug.select("#controlPoints").insert("circle", before) - .attr("cx", point[0]).attr("cy", point[1]).attr("r", .6) - .call(d3.drag().on("drag", dragControlPoint)) - .on("click", clickControlPoint); + debug.select('#controlPoints').insert('circle', before).attr('cx', point[0]).attr('cy', point[1]).attr('r', 0.6).call(d3.drag().on('drag', dragControlPoint)).on('click', clickControlPoint); } function addInterimControlPoint() { const point = d3.mouse(this); - const controls = document.getElementById("controlPoints").querySelectorAll("circle"); - const points = Array.from(controls).map(circle => [+circle.getAttribute("cx"), +circle.getAttribute("cy")]); + const controls = document.getElementById('controlPoints').querySelectorAll('circle'); + const points = Array.from(controls).map((circle) => [+circle.getAttribute('cx'), +circle.getAttribute('cy')]); const index = getSegmentId(points, point, 2); - addControlPoint(point, ":nth-child(" + (index+1) + ")"); + addControlPoint(point, ':nth-child(' + (index + 1) + ')'); redrawRoute(); } - + function dragControlPoint() { - this.setAttribute("cx", d3.event.x); - this.setAttribute("cy", d3.event.y); + this.setAttribute('cx', d3.event.x); + this.setAttribute('cy', d3.event.y); redrawRoute(); } function redrawRoute() { - lineGen.curve(d3.curveCatmullRom.alpha(.1)); + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); const points = []; - debug.select("#controlPoints").selectAll("circle").each(function() { - points.push([this.getAttribute("cx"), this.getAttribute("cy")]); - }); + debug + .select('#controlPoints') + .selectAll('circle') + .each(function () { + points.push([this.getAttribute('cx'), this.getAttribute('cy')]); + }); - elSelected.attr("d", round(lineGen(points))); + elSelected.attr('d', round(lineGen(points))); const l = elSelected.node().getTotalLength(); - routeLength.innerHTML = rn(l * distanceScaleInput.value) + " " + distanceUnitInput.value; + routeLength.innerHTML = rn(l * distanceScaleInput.value) + ' ' + distanceUnitInput.value; if (modules.elevation) showEPForRoute(elSelected.node()); } @@ -98,158 +99,169 @@ function editRoute(onClick) { } function showGroupSection() { - document.querySelectorAll("#routeEditor > button").forEach(el => el.style.display = "none"); - document.getElementById("routeGroupsSelection").style.display = "inline-block"; + document.querySelectorAll('#routeEditor > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('routeGroupsSelection').style.display = 'inline-block'; } function hideGroupSection() { - document.querySelectorAll("#routeEditor > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("routeGroupsSelection").style.display = "none"; - document.getElementById("routeGroupName").style.display = "none"; - document.getElementById("routeGroupName").value = ""; - document.getElementById("routeGroup").style.display = "inline-block"; + document.querySelectorAll('#routeEditor > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('routeGroupsSelection').style.display = 'none'; + document.getElementById('routeGroupName').style.display = 'none'; + document.getElementById('routeGroupName').value = ''; + document.getElementById('routeGroup').style.display = 'inline-block'; } function selectRouteGroup(node) { const group = node.parentNode.id; - const select = document.getElementById("routeGroup"); + const select = document.getElementById('routeGroup'); select.options.length = 0; // remove all options - routes.selectAll("g").each(function() { + routes.selectAll('g').each(function () { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); } - + function changeRouteGroup() { document.getElementById(this.value).appendChild(elSelected.node()); } - + function toggleNewGroupInput() { - if (routeGroupName.style.display === "none") { - routeGroupName.style.display = "inline-block"; + if (routeGroupName.style.display === 'none') { + routeGroupName.style.display = 'inline-block'; routeGroupName.focus(); - routeGroup.style.display = "none"; + routeGroup.style.display = 'none'; } else { - routeGroupName.style.display = "none"; - routeGroup.style.display = "inline-block"; - } + routeGroupName.style.display = 'none'; + routeGroup.style.display = 'inline-block'; + } } - + function createNewGroup() { - if (!this.value) {tip("Please provide a valid group name"); return;} - const group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, ""); + if (!this.value) { + tip('Please provide a valid group name'); + return; + } + const group = this.value + .toLowerCase() + .replace(/ /g, '_') + .replace(/[^\w\s]/gi, ''); if (document.getElementById(group)) { - tip("Element with this id already exists. Please provide a unique name", false, "error"); + tip('Element with this id already exists. Please provide a unique name', false, 'error'); return; } if (Number.isFinite(+group.charAt(0))) { - tip("Group name should start with a letter", false, "error"); + tip('Group name should start with a letter', false, 'error'); return; } // just rename if only 1 element left const oldGroup = elSelected.node().parentNode; - const basic = ["roads", "trails", "searoutes"].includes(oldGroup.id); + const basic = ['roads', 'trails', 'searoutes'].includes(oldGroup.id); if (!basic && oldGroup.childElementCount === 1) { - document.getElementById("routeGroup").selectedOptions[0].remove(); - document.getElementById("routeGroup").options.add(new Option(group, group, false, true)); + document.getElementById('routeGroup').selectedOptions[0].remove(); + document.getElementById('routeGroup').options.add(new Option(group, group, false, true)); oldGroup.id = group; toggleNewGroupInput(); - document.getElementById("routeGroupName").value = ""; + document.getElementById('routeGroupName').value = ''; return; } const newGroup = elSelected.node().parentNode.cloneNode(false); - document.getElementById("routes").appendChild(newGroup); + document.getElementById('routes').appendChild(newGroup); newGroup.id = group; - document.getElementById("routeGroup").options.add(new Option(group, group, false, true)); + document.getElementById('routeGroup').options.add(new Option(group, group, false, true)); document.getElementById(group).appendChild(elSelected.node()); toggleNewGroupInput(); - document.getElementById("routeGroupName").value = ""; + document.getElementById('routeGroupName').value = ''; } - + function removeRouteGroup() { const group = elSelected.node().parentNode.id; - const basic = ["roads", "trails", "searoutes"].includes(group); + const basic = ['roads', 'trails', 'searoutes'].includes(group); const count = elSelected.node().parentNode.childElementCount; - alertMessage.innerHTML = `Are you sure you want to remove - ${basic ? "all elements in the group" : "the entire route group"}? -

Routes to be removed: ${count}`; - $("#alert").dialog({resizable: false, title: "Remove route group", - buttons: { - Remove: function() { - $(this).dialog("close"); - $("#routeEditor").dialog("close"); - hideGroupSection(); - if (basic) routes.select("#"+group).selectAll("path").remove(); - else routes.select("#"+group).remove(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + + const message = `Are you sure you want to remove ${basic ? 'all elements in the group' : 'the entire route group'}?

Routes to be removed: ${count}`; + const onConfirm = () => { + $('#routeEditor').dialog('close'); + hideGroupSection(); + if (basic) + routes + .select('#' + group) + .selectAll('path') + .remove(); + else routes.select('#' + group).remove(); + }; + confirmationDialog({title: 'Remove route group', message, confirm: 'Remove', onConfirm}); } function editGroupStyle() { const g = elSelected.node().parentNode.id; - editStyle("routes", g); + editStyle('routes', g); } function toggleRouteSplitMode() { - document.getElementById("routeNew").classList.remove("pressed"); - this.classList.toggle("pressed"); + document.getElementById('routeNew').classList.remove('pressed'); + this.classList.toggle('pressed'); } function clickControlPoint() { - if (routeSplit.classList.contains("pressed")) splitRoute(this); - else {this.remove(); redrawRoute();} - } - - function splitRoute(clicked) { - lineGen.curve(d3.curveCatmullRom.alpha(.1)); - const group = d3.select(elSelected.node().parentNode); - routeSplit.classList.remove("pressed"); - - const points1 = [], points2 = []; - let points = points1; - debug.select("#controlPoints").selectAll("circle").each(function() { - points.push([this.getAttribute("cx"), this.getAttribute("cy")]); - if (this === clicked) { - points = points2; - points.push([this.getAttribute("cx"), this.getAttribute("cy")]); - } + if (routeSplit.classList.contains('pressed')) splitRoute(this); + else { this.remove(); - }); + redrawRoute(); + } + } - elSelected.attr("d", round(lineGen(points1))); - const id = getNextId("route"); - group.append("path").attr("id", id).attr("d", lineGen(points2)); - debug.select("#controlPoints").selectAll("circle").remove(); + function splitRoute(clicked) { + lineGen.curve(d3.curveCatmullRom.alpha(0.1)); + const group = d3.select(elSelected.node().parentNode); + routeSplit.classList.remove('pressed'); + + const points1 = [], + points2 = []; + let points = points1; + debug + .select('#controlPoints') + .selectAll('circle') + .each(function () { + points.push([this.getAttribute('cx'), this.getAttribute('cy')]); + if (this === clicked) { + points = points2; + points.push([this.getAttribute('cx'), this.getAttribute('cy')]); + } + this.remove(); + }); + + elSelected.attr('d', round(lineGen(points1))); + const id = getNextId('route'); + group.append('path').attr('id', id).attr('d', lineGen(points2)); + debug.select('#controlPoints').selectAll('circle').remove(); drawControlPoints(elSelected.node()); } function toggleRouteCreationMode() { - document.getElementById("routeSplit").classList.remove("pressed"); - document.getElementById("routeNew").classList.toggle("pressed"); - if (document.getElementById("routeNew").classList.contains("pressed")) { - tip("Click on map to add control points", true); - viewbox.on("click", addPointOnClick).style("cursor", "crosshair"); - elSelected.on("click", null); + document.getElementById('routeSplit').classList.remove('pressed'); + document.getElementById('routeNew').classList.toggle('pressed'); + if (document.getElementById('routeNew').classList.contains('pressed')) { + tip('Click on map to add control points', true); + viewbox.on('click', addPointOnClick).style('cursor', 'crosshair'); + elSelected.on('click', null); } else { clearMainTip(); - viewbox.on("click", clicked).style("cursor", "default"); - elSelected.on("click", addInterimControlPoint).attr("data-new", null); + viewbox.on('click', clicked).style('cursor', 'default'); + elSelected.on('click', addInterimControlPoint).attr('data-new', null); } } function addPointOnClick() { // create new route - if (!elSelected.attr("data-new")) { - debug.select("#controlPoints").selectAll("circle").remove(); + if (!elSelected.attr('data-new')) { + debug.select('#controlPoints').selectAll('circle').remove(); const parent = elSelected.node().parentNode; - const id = getNextId("route"); - elSelected = d3.select(parent).append("path").attr("id", id).attr("data-new", 1); + const id = getNextId('route'); + elSelected = d3.select(parent).append('path').attr('id', id).attr('data-new', 1); } addControlPoint(d3.mouse(this)); @@ -257,30 +269,25 @@ function editRoute(onClick) { } function editRouteLegend() { - const id = elSelected.attr("id"); + const id = elSelected.attr('id'); editNotes(id, id); } function removeRoute() { - alertMessage.innerHTML = "Are you sure you want to remove the route?"; - $("#alert").dialog({resizable: false, title: "Remove route", - buttons: { - Remove: function() { - $(this).dialog("close"); - elSelected.remove(); - $("#routeEditor").dialog("close"); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the route?
This action cannot be reverted'; + const onConfirm = () => { + elSelected.remove(); + $('#routeEditor').dialog('close'); + }; + confirmationDialog({title: 'Remove route', message, confirm: 'Remove', onConfirm}); } function closeRoutesEditor() { - elSelected.attr("data-new", null).on("click", null); + elSelected.attr('data-new', null).on('click', null); clearMainTip(); - routeSplit.classList.remove("pressed"); - routeNew.classList.remove("pressed"); - debug.select("#controlPoints").remove(); + routeSplit.classList.remove('pressed'); + routeNew.classList.remove('pressed'); + debug.select('#controlPoints').remove(); unselect(); } } diff --git a/modules/ui/states-editor.js b/modules/ui/states-editor.js index ffa86812..959028ed 100644 --- a/modules/ui/states-editor.js +++ b/modules/ui/states-editor.js @@ -1,63 +1,75 @@ -"use strict"; +'use strict'; function editStates() { if (customization) return; - closeDialogs("#statesEditor, .stable"); - if (!layerIsOn("toggleStates")) toggleStates(); - if (!layerIsOn("toggleBorders")) toggleBorders(); - if (layerIsOn("toggleCultures")) toggleCultures(); - if (layerIsOn("toggleBiomes")) toggleBiomes(); - if (layerIsOn("toggleReligions")) toggleReligions(); + closeDialogs('#statesEditor, .stable'); + if (!layerIsOn('toggleStates')) toggleStates(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + if (layerIsOn('toggleCultures')) toggleCultures(); + if (layerIsOn('toggleBiomes')) toggleBiomes(); + if (layerIsOn('toggleReligions')) toggleReligions(); - const body = document.getElementById("statesBodySection"); + const body = document.getElementById('statesBodySection'); refreshStatesEditor(); if (modules.editStates) return; modules.editStates = true; - $("#statesEditor").dialog({ - title: "States Editor", resizable: false, width: fitContent(), close: closeStatesEditor, - position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} + $('#statesEditor').dialog({ + title: 'States Editor', + resizable: false, + width: fitContent(), + close: closeStatesEditor, + position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'} }); // add listeners - document.getElementById("statesEditorRefresh").addEventListener("click", refreshStatesEditor); - document.getElementById("statesEditStyle").addEventListener("click", () => editStyle("regions")); - document.getElementById("statesLegend").addEventListener("click", toggleLegend); - document.getElementById("statesPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("statesChart").addEventListener("click", showStatesChart); - document.getElementById("statesRegenerate").addEventListener("click", openRegenerationMenu); - document.getElementById("statesRegenerateBack").addEventListener("click", exitRegenerationMenu); - document.getElementById("statesRecalculate").addEventListener("click", () => recalculateStates(true)); - document.getElementById("statesRandomize").addEventListener("click", randomizeStatesExpansion); - document.getElementById("statesNeutral").addEventListener("input", () => recalculateStates(false)); - document.getElementById("statesNeutralNumber").addEventListener("change", () => recalculateStates(false)); - document.getElementById("statesManually").addEventListener("click", enterStatesManualAssignent); - document.getElementById("statesManuallyApply").addEventListener("click", applyStatesManualAssignent); - document.getElementById("statesManuallyCancel").addEventListener("click", () => exitStatesManualAssignment()); - document.getElementById("statesAdd").addEventListener("click", enterAddStateMode); - document.getElementById("statesExport").addEventListener("click", downloadStatesData); + document.getElementById('statesEditorRefresh').addEventListener('click', refreshStatesEditor); + document.getElementById('statesEditStyle').addEventListener('click', () => editStyle('regions')); + document.getElementById('statesLegend').addEventListener('click', toggleLegend); + document.getElementById('statesPercentage').addEventListener('click', togglePercentageMode); + document.getElementById('statesChart').addEventListener('click', showStatesChart); + document.getElementById('statesRegenerate').addEventListener('click', openRegenerationMenu); + document.getElementById('statesRegenerateBack').addEventListener('click', exitRegenerationMenu); + document.getElementById('statesRecalculate').addEventListener('click', () => recalculateStates(true)); + document.getElementById('statesRandomize').addEventListener('click', randomizeStatesExpansion); + document.getElementById('statesNeutral').addEventListener('input', () => recalculateStates(false)); + document.getElementById('statesNeutralNumber').addEventListener('change', () => recalculateStates(false)); + document.getElementById('statesManually').addEventListener('click', enterStatesManualAssignent); + document.getElementById('statesManuallyApply').addEventListener('click', applyStatesManualAssignent); + document.getElementById('statesManuallyCancel').addEventListener('click', () => exitStatesManualAssignment()); + document.getElementById('statesAdd').addEventListener('click', enterAddStateMode); + document.getElementById('statesExport').addEventListener('click', downloadStatesData); - body.addEventListener("click", function(ev) { - const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id; - if (cl.contains("fillRect")) stateChangeFill(el); else - if (cl.contains("name")) editStateName(state); else - if (cl.contains("coaIcon")) editEmblem("state", "stateCOA"+state, pack.states[state]); else - if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state); else - if (cl.contains("culturePopulation")) changePopulation(state); else - if (cl.contains("icon-pin")) toggleFog(state, cl); else - if (cl.contains("icon-trash-empty")) stateRemovePrompt(state); + body.addEventListener('click', function (ev) { + const el = ev.target, + cl = el.classList, + line = el.parentNode, + state = +line.dataset.id; + if (cl.contains('fillRect')) stateChangeFill(el); + else if (cl.contains('name')) editStateName(state); + else if (cl.contains('coaIcon')) editEmblem('state', 'stateCOA' + state, pack.states[state]); + else if (cl.contains('icon-star-empty')) stateCapitalZoomIn(state); + else if (cl.contains('culturePopulation')) changePopulation(state); + else if (cl.contains('icon-pin')) toggleFog(state, cl); + else if (cl.contains('icon-trash-empty')) stateRemovePrompt(state); }); - body.addEventListener("input", function(ev) { - const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id; - if (cl.contains("stateCapital")) stateChangeCapitalName(state, line, el.value); else - if (cl.contains("cultureType")) stateChangeType(state, line, el.value); else - if (cl.contains("statePower")) stateChangeExpansionism(state, line, el.value); + body.addEventListener('input', function (ev) { + const el = ev.target, + cl = el.classList, + line = el.parentNode, + state = +line.dataset.id; + if (cl.contains('stateCapital')) stateChangeCapitalName(state, line, el.value); + else if (cl.contains('cultureType')) stateChangeType(state, line, el.value); + else if (cl.contains('statePower')) stateChangeExpansionism(state, line, el.value); }); - body.addEventListener("change", function(ev) { - const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id; - if (cl.contains("stateCulture")) stateChangeCulture(state, line, el.value); + body.addEventListener('change', function (ev) { + const el = ev.target, + cl = el.classList, + line = el.parentNode, + state = +line.dataset.id; + if (cl.contains('stateCulture')) stateChangeCulture(state, line, el.value); }); function refreshStatesEditor() { @@ -67,13 +79,16 @@ function editStates() { // add line for each state function statesEditorAddLines() { - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - const hidden = statesRegenerateButtons.style.display === "block" ? "" : "hidden"; // show/hide regenerate columns - let lines = "", totalArea = 0, totalPopulation = 0, totalBurgs = 0; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + const hidden = statesRegenerateButtons.style.display === 'block' ? '' : 'hidden'; // show/hide regenerate columns + let lines = '', + totalArea = 0, + totalPopulation = 0, + totalBurgs = 0; for (const s of pack.states) { if (s.removed) continue; - const area = s.area * (distanceScaleInput.value ** 2); + const area = s.area * distanceScaleInput.value ** 2; const rural = s.rural * populationRate.value; const urban = s.urban * populationRate.value * urbanization.value; const population = rn(rural + urban); @@ -81,7 +96,7 @@ function editStates() { totalArea += area; totalPopulation += population; totalBurgs += s.burgs; - const focused = defs.select("#fog #focusState"+s.i).size(); + const focused = defs.select('#fog #focusState' + s.i).size(); if (!s.i) { // Neutral line @@ -110,10 +125,12 @@ function editStates() { } const capital = pack.burgs[s.capital].name; - COArenderer.trigger("stateCOA"+s.i, s.coa); + COArenderer.trigger('stateCOA' + s.i, s.coa); lines += `
- + @@ -128,165 +145,189 @@ function editStates() {
${si(population)}
- +
${s.cells}
- +
`; } body.innerHTML = lines; // update footer - statesFooterStates.innerHTML = pack.states.filter(s => s.i && !s.removed).length; - statesFooterCells.innerHTML = pack.cells.h.filter(h => h >= 20).length; + statesFooterStates.innerHTML = pack.states.filter((s) => s.i && !s.removed).length; + statesFooterCells.innerHTML = pack.cells.h.filter((h) => h >= 20).length; statesFooterBurgs.innerHTML = totalBurgs; statesFooterArea.innerHTML = si(totalArea) + unit; statesFooterPopulation.innerHTML = si(totalPopulation); statesFooterArea.dataset.area = totalArea; statesFooterPopulation.dataset.population = totalPopulation; - body.querySelectorAll("div.states").forEach(el => { - el.addEventListener("click", selectStateOnLineClick); - el.addEventListener("mouseenter", ev => stateHighlightOn(ev)); - el.addEventListener("mouseleave", ev => stateHighlightOff(ev)); + body.querySelectorAll('div.states').forEach((el) => { + el.addEventListener('click', selectStateOnLineClick); + el.addEventListener('mouseenter', (ev) => stateHighlightOn(ev)); + el.addEventListener('mouseleave', (ev) => stateHighlightOff(ev)); }); - if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();} + if (body.dataset.type === 'percentage') { + body.dataset.type = 'absolute'; + togglePercentageMode(); + } applySorting(statesHeader); - $("#statesEditor").dialog({width: fitContent()}); + $('#statesEditor').dialog({width: fitContent()}); } - + function getCultureOptions(culture) { - let options = ""; - pack.cultures.forEach(c => {if (!c.removed) { options += `` }}); + let options = ''; + pack.cultures.forEach((c) => { + if (!c.removed) { + options += ``; + } + }); return options; } function getTypeOptions(type) { - let options = ""; - const types = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; - types.forEach(t => options += ``); + let options = ''; + const types = ['Generic', 'River', 'Lake', 'Naval', 'Nomadic', 'Hunting', 'Highland']; + types.forEach((t) => (options += ``)); return options; } function stateHighlightOn(event) { - if (!layerIsOn("toggleStates")) return; - if (defs.select("#fog path").size()) return; + if (!layerIsOn('toggleStates')) return; + if (defs.select('#fog path').size()) return; const state = +event.target.dataset.id; if (customization || !state) return; - const d = regions.select("#state"+state).attr("d"); + const d = regions.select('#state' + state).attr('d'); - const path = debug.append("path").attr("class", "highlight").attr("d", d) - .attr("fill", "none").attr("stroke", "red").attr("stroke-width", 1).attr("opacity", 1) - .attr("filter", "url(#blur1)"); + const path = debug.append('path').attr('class', 'highlight').attr('d', d).attr('fill', 'none').attr('stroke', 'red').attr('stroke-width', 1).attr('opacity', 1).attr('filter', 'url(#blur1)'); - const l = path.node().getTotalLength(), dur = (l + 5000) / 2; - const i = d3.interpolateString("0," + l, l + "," + l); - path.transition().duration(dur).attrTween("stroke-dasharray", function() {return t => i(t)}); + const l = path.node().getTotalLength(), + dur = (l + 5000) / 2; + const i = d3.interpolateString('0,' + l, l + ',' + l); + path + .transition() + .duration(dur) + .attrTween('stroke-dasharray', function () { + return (t) => i(t); + }); } function stateHighlightOff() { - debug.selectAll(".highlight").each(function() { - d3.select(this).transition().duration(1000).attr("opacity", 0).remove(); + debug.selectAll('.highlight').each(function () { + d3.select(this).transition().duration(1000).attr('opacity', 0).remove(); }); } function stateChangeFill(el) { - const currentFill = el.getAttribute("fill"); + const currentFill = el.getAttribute('fill'); const state = +el.parentNode.parentNode.dataset.id; - const callback = function(fill) { - el.setAttribute("fill", fill); + const callback = function (fill) { + el.setAttribute('fill', fill); pack.states[state].color = fill; - statesBody.select("#state"+state).attr("fill", fill); - statesBody.select("#state-gap"+state).attr("stroke", fill); - const halo = d3.color(fill) ? d3.color(fill).darker().hex() : "#666666"; - statesHalo.select("#state-border"+state).attr("stroke", halo); + statesBody.select('#state' + state).attr('fill', fill); + statesBody.select('#state-gap' + state).attr('stroke', fill); + const halo = d3.color(fill) ? d3.color(fill).darker().hex() : '#666666'; + statesHalo.select('#state-border' + state).attr('stroke', halo); // recolor regiments - const solidColor = fill[0] === "#" ? fill : "#999"; + const solidColor = fill[0] === '#' ? fill : '#999'; const darkerColor = d3.color(solidColor).darker().hex(); - armies.select("#army"+state).attr("fill", solidColor); - armies.select("#army"+state).selectAll("g > rect:nth-of-type(2)").attr("fill", darkerColor); - } + armies.select('#army' + state).attr('fill', solidColor); + armies + .select('#army' + state) + .selectAll('g > rect:nth-of-type(2)') + .attr('fill', darkerColor); + }; openPicker(currentFill, callback); } function editStateName(state) { // reset input value and close add mode - stateNameEditorCustomForm.value = ""; - const addModeActive = stateNameEditorCustomForm.style.display === "inline-block"; + stateNameEditorCustomForm.value = ''; + const addModeActive = stateNameEditorCustomForm.style.display === 'inline-block'; if (addModeActive) { - stateNameEditorCustomForm.style.display = "none"; - stateNameEditorSelectForm.style.display = "inline-block"; + stateNameEditorCustomForm.style.display = 'none'; + stateNameEditorSelectForm.style.display = 'inline-block'; } const s = pack.states[state]; - document.getElementById("stateNameEditor").dataset.state = state; - document.getElementById("stateNameEditorShort").value = s.name || ""; + document.getElementById('stateNameEditor').dataset.state = state; + document.getElementById('stateNameEditorShort').value = s.name || ''; applyOption(stateNameEditorSelectForm, s.formName); - document.getElementById("stateNameEditorFull").value = s.fullName || ""; + document.getElementById('stateNameEditorFull').value = s.fullName || ''; - $("#stateNameEditor").dialog({ - resizable: false, title: "Change state name", buttons: { - Apply: function() {applyNameChange(s); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");} - }, position: {my: "center", at: "center", of: "svg"} + $('#stateNameEditor').dialog({ + resizable: false, + title: 'Change state name', + buttons: { + Apply: function () { + applyNameChange(s); + $(this).dialog('close'); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); if (modules.editStateName) return; modules.editStateName = true; // add listeners - document.getElementById("stateNameEditorShortCulture").addEventListener("click", regenerateShortNameCuture); - document.getElementById("stateNameEditorShortRandom").addEventListener("click", regenerateShortNameRandom); - document.getElementById("stateNameEditorAddForm").addEventListener("click", addCustomForm); - document.getElementById("stateNameEditorCustomForm").addEventListener("change", addCustomForm); - document.getElementById("stateNameEditorFullRegenerate").addEventListener("click", regenerateFullName); + document.getElementById('stateNameEditorShortCulture').addEventListener('click', regenerateShortNameCuture); + document.getElementById('stateNameEditorShortRandom').addEventListener('click', regenerateShortNameRandom); + document.getElementById('stateNameEditorAddForm').addEventListener('click', addCustomForm); + document.getElementById('stateNameEditorCustomForm').addEventListener('change', addCustomForm); + document.getElementById('stateNameEditorFullRegenerate').addEventListener('click', regenerateFullName); function regenerateShortNameCuture() { const state = +stateNameEditor.dataset.state; const culture = pack.states[state].culture; const name = Names.getState(Names.getCultureShort(culture), culture); - document.getElementById("stateNameEditorShort").value = name; + document.getElementById('stateNameEditorShort').value = name; } function regenerateShortNameRandom() { - const base = rand(nameBases.length-1); + const base = rand(nameBases.length - 1); const name = Names.getState(Names.getBase(base), undefined, base); - document.getElementById("stateNameEditorShort").value = name; + document.getElementById('stateNameEditorShort').value = name; } function addCustomForm() { const value = stateNameEditorCustomForm.value; - const addModeActive = stateNameEditorCustomForm.style.display === "inline-block"; - stateNameEditorCustomForm.style.display = addModeActive ? "none" : "inline-block"; - stateNameEditorSelectForm.style.display = addModeActive ? "inline-block" : "none"; + const addModeActive = stateNameEditorCustomForm.style.display === 'inline-block'; + stateNameEditorCustomForm.style.display = addModeActive ? 'none' : 'inline-block'; + stateNameEditorSelectForm.style.display = addModeActive ? 'inline-block' : 'none'; if (value && addModeActive) applyOption(stateNameEditorSelectForm, value); - stateNameEditorCustomForm.value = ""; + stateNameEditorCustomForm.value = ''; } function regenerateFullName() { - const short = document.getElementById("stateNameEditorShort").value; - const form = document.getElementById("stateNameEditorSelectForm").value; - document.getElementById("stateNameEditorFull").value = getFullName(); + const short = document.getElementById('stateNameEditorShort').value; + const form = document.getElementById('stateNameEditorSelectForm').value; + document.getElementById('stateNameEditorFull').value = getFullName(); function getFullName() { if (!form) return short; - if (!short && form) return "The " + form; + if (!short && form) return 'The ' + form; const tick = +stateNameEditorFullRegenerate.dataset.tick; - stateNameEditorFullRegenerate.dataset.tick = tick+1; - return tick%2 ? getAdjective(short) + " " + form : form + " of " + short; + stateNameEditorFullRegenerate.dataset.tick = tick + 1; + return tick % 2 ? getAdjective(short) + ' ' + form : form + ' of ' + short; } } function applyNameChange(s) { - const nameInput = document.getElementById("stateNameEditorShort"); - const formSelect = document.getElementById("stateNameEditorSelectForm"); - const fullNameInput = document.getElementById("stateNameEditorFull"); + const nameInput = document.getElementById('stateNameEditorShort'); + const formSelect = document.getElementById('stateNameEditorSelectForm'); + const fullNameInput = document.getElementById('stateNameEditorFull'); const nameChanged = nameInput.value !== s.name; const formChanged = formSelect.value !== s.formName; @@ -312,73 +353,85 @@ function editStates() { const capital = pack.states[state].capital; if (!capital) return; pack.burgs[capital].name = value; - document.querySelector("#burgLabel"+capital).textContent = value; + document.querySelector('#burgLabel' + capital).textContent = value; } function changePopulation(state) { const s = pack.states[state]; - if (!s.cells) {tip("State does not have any cells, cannot change population", false, "error"); return;} + if (!s.cells) { + tip('State does not have any cells, cannot change population', false, 'error'); + return; + } const rural = rn(s.rural * populationRate.value); const urban = rn(s.urban * populationRate.value * urbanization.value); const total = rural + urban; - const l = n => Number(n).toLocaleString(); + const l = (n) => Number(n).toLocaleString(); alertMessage.innerHTML = ` Rural: - Urban: + Urban:

Total population: ${l(total)} ⇒ ${l(total)} (100%)

`; - const update = function() { + const update = function () { const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber; if (isNaN(totalNew)) return; totalPop.innerHTML = l(totalNew); - totalPopPerc.innerHTML = rn(totalNew / total * 100); - } + totalPopPerc.innerHTML = rn((totalNew / total) * 100); + }; ruralPop.oninput = () => update(); urbanPop.oninput = () => update(); - $("#alert").dialog({ - resizable: false, title: "Change state population", width: "24em", buttons: { - Apply: function() {applyPopulationChange(); $(this).dialog("close");}, - Cancel: function() {$(this).dialog("close");} - }, position: {my: "center", at: "center", of: "svg"} + $('#alert').dialog({ + resizable: false, + title: 'Change state population', + width: '24em', + buttons: { + Apply: function () { + applyPopulationChange(); + $(this).dialog('close'); + }, + Cancel: function () { + $(this).dialog('close'); + } + }, + position: {my: 'center', at: 'center', of: 'svg'} }); function applyPopulationChange() { const ruralChange = ruralPop.value / rural; if (isFinite(ruralChange) && ruralChange !== 1) { - const cells = pack.cells.i.filter(i => pack.cells.state[i] === state); - cells.forEach(i => pack.cells.pop[i] *= ruralChange); + const cells = pack.cells.i.filter((i) => pack.cells.state[i] === state); + cells.forEach((i) => (pack.cells.pop[i] *= ruralChange)); } if (!isFinite(ruralChange) && +ruralPop.value > 0) { const points = ruralPop.value / populationRate.value; - const cells = pack.cells.i.filter(i => pack.cells.state[i] === state); + const cells = pack.cells.i.filter((i) => pack.cells.state[i] === state); const pop = points / cells.length; - cells.forEach(i => pack.cells.pop[i] = pop); + cells.forEach((i) => (pack.cells.pop[i] = pop)); } const urbanChange = urbanPop.value / urban; if (isFinite(urbanChange) && urbanChange !== 1) { - const burgs = pack.burgs.filter(b => !b.removed && b.state === state); - burgs.forEach(b => b.population = rn(b.population * urbanChange, 4)); + const burgs = pack.burgs.filter((b) => !b.removed && b.state === state); + burgs.forEach((b) => (b.population = rn(b.population * urbanChange, 4))); } if (!isFinite(urbanChange) && +urbanPop.value > 0) { const points = urbanPop.value / populationRate.value / urbanization.value; - const burgs = pack.burgs.filter(b => !b.removed && b.state === state); + const burgs = pack.burgs.filter((b) => !b.removed && b.state === state); const population = rn(points / burgs.length, 4); - burgs.forEach(b => b.population = population); + burgs.forEach((b) => (b.population = population)); } refreshStatesEditor(); } - } function stateCapitalZoomIn(state) { const capital = pack.states[state].capital; const l = burgLabels.select("[data-id='" + capital + "']"); - const x = +l.attr("x"), y = +l.attr("y"); + const x = +l.attr('x'), + y = +l.attr('y'); zoomTo(x, y, 8, 2000); } @@ -392,115 +445,126 @@ function editStates() { } function stateChangeExpansionism(state, line, value) { - line.dataset.expansionism = pack.states[state].expansionism = value; + line.dataset.expansionism = pack.states[state].expansionism = value; recalculateStates(); } function toggleFog(state, cl) { if (customization) return; - const path = statesBody.select("#state"+state).attr("d"), id = "focusState"+state; - cl.contains("inactive") ? fog(id, path) : unfog(id); - cl.toggle("inactive"); + const path = statesBody.select('#state' + state).attr('d'), + id = 'focusState' + state; + cl.contains('inactive') ? fog(id, path) : unfog(id); + cl.toggle('inactive'); } function stateRemovePrompt(state) { if (customization) return; - - alertMessage.innerHTML = "Are you sure you want to remove the state?
This action cannot be reverted"; - $("#alert").dialog({resizable: false, title: "Remove state", - buttons: { - Remove: function() { - $(this).dialog("close"); - stateRemove(state); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + const message = 'Are you sure you want to remove the state?
This action cannot be reverted'; + confirmationDialog({title: 'Remove state', message, confirm: 'Remove', onConfirm: () => stateRemove(state)}); } function stateRemove(state) { - statesBody.select("#state"+state).remove(); - statesBody.select("#state-gap"+state).remove(); - statesHalo.select("#state-border"+state).remove(); - labels.select("#stateLabel"+state).remove(); - defs.select("#textPath_stateLabel"+state).remove(); + statesBody.select('#state' + state).remove(); + statesBody.select('#state-gap' + state).remove(); + statesHalo.select('#state-border' + state).remove(); + labels.select('#stateLabel' + state).remove(); + defs.select('#textPath_stateLabel' + state).remove(); - unfog("focusState"+state); - pack.burgs.forEach(b => {if(b.state === state) b.state = 0;}); - pack.cells.state.forEach((s, i) => {if(s === state) pack.cells.state[i] = 0;}); + unfog('focusState' + state); + pack.burgs.forEach((b) => { + if (b.state === state) b.state = 0; + }); + pack.cells.state.forEach((s, i) => { + if (s === state) pack.cells.state[i] = 0; + }); // remove emblem - const coaId = "stateCOA" + state; + const coaId = 'stateCOA' + state; document.getElementById(coaId).remove(); emblems.select(`#stateEmblems > use[data-i='${state}']`).remove(); // remove provinces - pack.states[state].provinces.forEach(p => { + pack.states[state].provinces.forEach((p) => { pack.provinces[p] = {i: p, removed: true}; - pack.cells.province.forEach((pr, i) => {if(pr === p) pack.cells.province[i] = 0;}); - const coaId = "provinceCOA" + p; + pack.cells.province.forEach((pr, i) => { + if (pr === p) pack.cells.province[i] = 0; + }); + const coaId = 'provinceCOA' + p; if (document.getElementById(coaId)) document.getElementById(coaId).remove(); emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove(); - const g = provs.select("#provincesBody"); - g.select("#province"+p).remove(); - g.select("#province-gap"+p).remove(); + const g = provs.select('#provincesBody'); + g.select('#province' + p).remove(); + g.select('#province-gap' + p).remove(); }); // remove military - pack.states[state].military.forEach(m => { + pack.states[state].military.forEach((m) => { const id = `regiment${state}-${m.i}`; - const index = notes.findIndex(n => n.id === id); + const index = notes.findIndex((n) => n.id === id); if (index != -1) notes.splice(index, 1); }); - armies.select("g#army"+state).remove(); + armies.select('g#army' + state).remove(); const capital = pack.states[state].capital; pack.burgs[capital].capital = 0; pack.burgs[capital].state = 0; - moveBurgToGroup(capital, "towns"); + moveBurgToGroup(capital, 'towns'); pack.states[state] = {i: state, removed: true}; - debug.selectAll(".highlight").remove(); - if (!layerIsOn("toggleStates")) toggleStates(); else drawStates(); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - if (layerIsOn("toggleProvinces")) drawProvinces(); + debug.selectAll('.highlight').remove(); + if (!layerIsOn('toggleStates')) toggleStates(); + else drawStates(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + if (layerIsOn('toggleProvinces')) drawProvinces(); refreshStatesEditor(); } function toggleLegend() { - if (legend.selectAll("*").size()) {clearLegend(); return;}; // hide legend - const data = pack.states.filter(s => s.i && !s.removed && s.cells).sort((a, b) => b.area - a.area).map(s => [s.i, s.color, s.name]); - drawLegend("States", data); + if (legend.selectAll('*').size()) { + clearLegend(); + return; + } // hide legend + const data = pack.states + .filter((s) => s.i && !s.removed && s.cells) + .sort((a, b) => b.area - a.area) + .map((s) => [s.i, s.color, s.name]); + drawLegend('States', data); } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; + if (body.dataset.type === 'absolute') { + body.dataset.type = 'percentage'; const totalCells = +statesFooterCells.innerHTML; const totalBurgs = +statesFooterBurgs.innerHTML; const totalArea = +statesFooterArea.dataset.area; const totalPopulation = +statesFooterPopulation.dataset.population; - body.querySelectorAll(":scope > div").forEach(function(el) { - el.querySelector(".stateCells").innerHTML = rn(+el.dataset.cells / totalCells * 100) + "%"; - el.querySelector(".stateBurgs").innerHTML = rn(+el.dataset.burgs / totalBurgs * 100) + "%"; - el.querySelector(".biomeArea").innerHTML = rn(+el.dataset.area / totalArea * 100) + "%"; - el.querySelector(".culturePopulation").innerHTML = rn(+el.dataset.population / totalPopulation * 100) + "%"; + body.querySelectorAll(':scope > div').forEach(function (el) { + el.querySelector('.stateCells').innerHTML = rn((+el.dataset.cells / totalCells) * 100) + '%'; + el.querySelector('.stateBurgs').innerHTML = rn((+el.dataset.burgs / totalBurgs) * 100) + '%'; + el.querySelector('.biomeArea').innerHTML = rn((+el.dataset.area / totalArea) * 100) + '%'; + el.querySelector('.culturePopulation').innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + '%'; }); } else { - body.dataset.type = "absolute"; + body.dataset.type = 'absolute'; statesEditorAddLines(); } } function showStatesChart() { // build hierarchy tree - const data = pack.states.filter(s => !s.removed); - const root = d3.stratify().id(d => d.i).parentId(d => d.i ? 0 : null)(data) - .sum(d => d.area).sort((a, b) => b.value - a.value); + const data = pack.states.filter((s) => !s.removed); + const root = d3 + .stratify() + .id((d) => d.i) + .parentId((d) => (d.i ? 0 : null))(data) + .sum((d) => d.area) + .sort((a, b) => b.value - a.value); - const width = 150 + 200 * uiSizeOutput.value, height = 150 + 200 * uiSizeOutput.value; + const width = 150 + 200 * uiSizeOutput.value, + height = 150 + 200 * uiSizeOutput.value; const margin = {top: 0, right: -50, bottom: 0, left: -50}; const w = width - margin.left - margin.right; const h = height - margin.top - margin.bottom; @@ -515,46 +579,68 @@ function editStates() { `; alertMessage.innerHTML += `
`; - const svg = d3.select("#alertMessage").insert("svg", "#statesInfo").attr("id", "statesTree") - .attr("width", width).attr("height", height).style("font-family", "Almendra SC") - .attr("text-anchor", "middle").attr("dominant-baseline", "central"); - const graph = svg.append("g").attr("transform", `translate(-50, 0)`); - document.getElementById("statesTreeType").addEventListener("change", updateChart); + const svg = d3 + .select('#alertMessage') + .insert('svg', '#statesInfo') + .attr('id', 'statesTree') + .attr('width', width) + .attr('height', height) + .style('font-family', 'Almendra SC') + .attr('text-anchor', 'middle') + .attr('dominant-baseline', 'central'); + const graph = svg.append('g').attr('transform', `translate(-50, 0)`); + document.getElementById('statesTreeType').addEventListener('change', updateChart); treeLayout(root); - const node = graph.selectAll("g").data(root.leaves()).enter() - .append("g").attr("transform", d => `translate(${d.x},${d.y})`) - .attr("data-id", d => d.data.i) - .on("mouseenter", d => showInfo(event, d)) - .on("mouseleave", d => hideInfo(event, d)); + const node = graph + .selectAll('g') + .data(root.leaves()) + .enter() + .append('g') + .attr('transform', (d) => `translate(${d.x},${d.y})`) + .attr('data-id', (d) => d.data.i) + .on('mouseenter', (d) => showInfo(event, d)) + .on('mouseleave', (d) => hideInfo(event, d)); - node.append("circle").attr("fill", d => d.data.color).attr("r", d => d.r); + node + .append('circle') + .attr('fill', (d) => d.data.color) + .attr('r', (d) => d.r); const exp = /(?=[A-Z][^A-Z])/g; - const lp = n => d3.max(n.split(exp).map(p => p.length)) + 1; // longest name part + 1 + const lp = (n) => d3.max(n.split(exp).map((p) => p.length)) + 1; // longest name part + 1 - node.append("text") - .style("font-size", d => rn(d.r ** .97 * 4 / lp(d.data.name), 2) + "px") - .selectAll("tspan").data(d => d.data.name.split(exp)) - .join("tspan").attr("x", 0).text(d => d) - .attr("dy", (d, i, n) => `${i ? 1 : (n.length-1) / -2}em`); + node + .append('text') + .style('font-size', (d) => rn((d.r ** 0.97 * 4) / lp(d.data.name), 2) + 'px') + .selectAll('tspan') + .data((d) => d.data.name.split(exp)) + .join('tspan') + .attr('x', 0) + .text((d) => d) + .attr('dy', (d, i, n) => `${i ? 1 : (n.length - 1) / -2}em`); function showInfo(ev, d) { - d3.select(ev.target).select("circle").classed("selected", 1); + d3.select(ev.target).select('circle').classed('selected', 1); const state = d.data.fullName; - const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; - const area = d.data.area * (distanceScaleInput.value ** 2) + unit; + const unit = areaUnit.value === 'square' ? ' ' + distanceUnitInput.value + '²' : ' ' + areaUnit.value; + const area = d.data.area * distanceScaleInput.value ** 2 + unit; const rural = rn(d.data.rural * populationRate.value); const urban = rn(d.data.urban * populationRate.value * urbanization.value); const option = statesTreeType.value; - const value = option === "area" ? "Area: " + area - : option === "rural" ? "Rural population: " + si(rural) - : option === "urban" ? "Urban population: " + si(urban) - : option === "burgs" ? "Burgs number: " + d.data.burgs - : "Population: " + si(rural + urban); + const value = + option === 'area' + ? 'Area: ' + area + : option === 'rural' + ? 'Rural population: ' + si(rural) + : option === 'urban' + ? 'Urban population: ' + si(urban) + : option === 'burgs' + ? 'Burgs number: ' + d.data.burgs + : 'Population: ' + si(rural + urban); statesInfo.innerHTML = `${state}. ${value}`; stateHighlightOn(ev); @@ -562,40 +648,51 @@ function editStates() { function hideInfo(ev) { stateHighlightOff(ev); - if (!document.getElementById("statesInfo")) return; - statesInfo.innerHTML = "‍"; - d3.select(ev.target).select("circle").classed("selected", 0); + if (!document.getElementById('statesInfo')) return; + statesInfo.innerHTML = '‍'; + d3.select(ev.target).select('circle').classed('selected', 0); } function updateChart() { - const value = this.value === "area" ? d => d.area - : this.value === "rural" ? d => d.rural - : this.value === "urban" ? d => d.urban - : this.value === "burgs" ? d => d.burgs - : d => d.rural + d.urban; + const value = + this.value === 'area' ? (d) => d.area : this.value === 'rural' ? (d) => d.rural : this.value === 'urban' ? (d) => d.urban : this.value === 'burgs' ? (d) => d.burgs : (d) => d.rural + d.urban; root.sum(value); node.data(treeLayout(root).leaves()); - node.transition().duration(1500).attr("transform", d => `translate(${d.x},${d.y})`) - node.select("circle").transition().duration(1500).attr("r", d => d.r); - node.select("text").transition().duration(1500) - .style("font-size", d => rn(d.r ** .97 * 4 / lp(d.data.name), 2) + "px"); + node + .transition() + .duration(1500) + .attr('transform', (d) => `translate(${d.x},${d.y})`); + node + .select('circle') + .transition() + .duration(1500) + .attr('r', (d) => d.r); + node + .select('text') + .transition() + .duration(1500) + .style('font-size', (d) => rn((d.r ** 0.97 * 4) / lp(d.data.name), 2) + 'px'); } - $("#alert").dialog({ - title: "States bubble chart", width: fitContent(), - position: {my: "left bottom", at: "left+10 bottom-10", of: "svg"}, buttons: {}, - close: () => {alertMessage.innerHTML = "";} + $('#alert').dialog({ + title: 'States bubble chart', + width: fitContent(), + position: {my: 'left bottom', at: 'left+10 bottom-10', of: 'svg'}, + buttons: {}, + close: () => { + alertMessage.innerHTML = ''; + } }); } function openRegenerationMenu() { - statesBottom.querySelectorAll(":scope > button").forEach(el => el.style.display = "none"); - statesRegenerateButtons.style.display = "block"; + statesBottom.querySelectorAll(':scope > button').forEach((el) => (el.style.display = 'none')); + statesRegenerateButtons.style.display = 'block'; - statesEditor.querySelectorAll(".show").forEach(el => el.classList.remove("hidden")); - $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); + statesEditor.querySelectorAll('.show').forEach((el) => el.classList.remove('hidden')); + $('#statesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); } function recalculateStates(must) { @@ -603,57 +700,56 @@ function editStates() { BurgsAndStates.expandStates(); BurgsAndStates.generateProvinces(); - if (!layerIsOn("toggleStates")) toggleStates(); else drawStates(); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - if (layerIsOn("toggleProvinces")) drawProvinces(); + if (!layerIsOn('toggleStates')) toggleStates(); + else drawStates(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + if (layerIsOn('toggleProvinces')) drawProvinces(); if (adjustLabels.checked) BurgsAndStates.drawStateLabels(); refreshStatesEditor(); } function randomizeStatesExpansion() { - pack.states.forEach(s => { + pack.states.forEach((s) => { if (!s.i || s.removed) return; const expansionism = rn(Math.random() * 4 + 1, 1); s.expansionism = expansionism; - body.querySelector("div.states[data-id='"+s.i+"'] > input.statePower").value = expansionism; + body.querySelector("div.states[data-id='" + s.i + "'] > input.statePower").value = expansionism; }); recalculateStates(true, true); } function exitRegenerationMenu() { - statesBottom.querySelectorAll(":scope > button").forEach(el => el.style.display = "inline-block"); - statesRegenerateButtons.style.display = "none"; - statesEditor.querySelectorAll(".show").forEach(el => el.classList.add("hidden")); - $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); + statesBottom.querySelectorAll(':scope > button').forEach((el) => (el.style.display = 'inline-block')); + statesRegenerateButtons.style.display = 'none'; + statesEditor.querySelectorAll('.show').forEach((el) => el.classList.add('hidden')); + $('#statesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); } function enterStatesManualAssignent() { - if (!layerIsOn("toggleStates")) toggleStates(); + if (!layerIsOn('toggleStates')) toggleStates(); customization = 2; - statesBody.append("g").attr("id", "temp"); - document.querySelectorAll("#statesBottom > button").forEach(el => el.style.display = "none"); - document.getElementById("statesManuallyButtons").style.display = "inline-block"; - document.getElementById("statesHalo").style.display = "none"; + statesBody.append('g').attr('id', 'temp'); + document.querySelectorAll('#statesBottom > button').forEach((el) => (el.style.display = 'none')); + document.getElementById('statesManuallyButtons').style.display = 'inline-block'; + document.getElementById('statesHalo').style.display = 'none'; - statesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); - statesFooter.style.display = "none"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); - $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); + statesEditor.querySelectorAll('.hide').forEach((el) => el.classList.add('hidden')); + statesFooter.style.display = 'none'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); + $('#statesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); - tip("Click on state to select, drag the circle to change state", true); - viewbox.style("cursor", "crosshair") - .on("click", selectStateOnMapClick) - .call(d3.drag().on("start", dragStateBrush)) - .on("touchmove mousemove", moveStateBrush); + tip('Click on state to select, drag the circle to change state', true); + viewbox.style('cursor', 'crosshair').on('click', selectStateOnMapClick).call(d3.drag().on('start', dragStateBrush)).on('touchmove mousemove', moveStateBrush); - body.querySelector("div").classList.add("selected"); + body.querySelector('div').classList.add('selected'); } function selectStateOnLineClick() { if (customization !== 2) return; - if (this.parentNode.id !== "statesBodySection") return; - body.querySelector("div.selected").classList.remove("selected"); - this.classList.add("selected"); + if (this.parentNode.id !== 'statesBodySection') return; + body.querySelector('div.selected').classList.remove('selected'); + this.classList.add('selected'); } function selectStateOnMapClick() { @@ -661,17 +757,17 @@ function editStates() { const i = findCell(point[0], point[1]); if (pack.cells.h[i] < 20) return; - const assigned = statesBody.select("#temp").select("polygon[data-cell='"+i+"']"); - const state = assigned.size() ? +assigned.attr("data-state") : pack.cells.state[i]; + const assigned = statesBody.select('#temp').select("polygon[data-cell='" + i + "']"); + const state = assigned.size() ? +assigned.attr('data-state') : pack.cells.state[i]; - body.querySelector("div.selected").classList.remove("selected"); - body.querySelector("div[data-id='"+state+"']").classList.add("selected"); + body.querySelector('div.selected').classList.remove('selected'); + body.querySelector("div[data-id='" + state + "']").classList.add('selected'); } function dragStateBrush() { const r = +statesManuallyBrush.value; - d3.event.on("drag", () => { + d3.event.on('drag', () => { if (!d3.event.dx && !d3.event.dy) return; const p = d3.mouse(this); moveCircle(p[0], p[1], r); @@ -684,21 +780,21 @@ function editStates() { // change state within selection function changeStateForSelection(selection) { - const temp = statesBody.select("#temp"); - const selected = body.querySelector("div.selected"); + const temp = statesBody.select('#temp'); + const selected = body.querySelector('div.selected'); const stateNew = +selected.dataset.id; - const color = pack.states[stateNew].color || "#ffffff"; + const color = pack.states[stateNew].color || '#ffffff'; - selection.forEach(function(i) { - const exists = temp.select("polygon[data-cell='"+i+"']"); - const stateOld = exists.size() ? +exists.attr("data-state") : pack.cells.state[i]; + selection.forEach(function (i) { + const exists = temp.select("polygon[data-cell='" + i + "']"); + const stateOld = exists.size() ? +exists.attr('data-state') : pack.cells.state[i]; if (stateNew === stateOld) return; if (i === pack.states[stateOld].center) return; // change of append new element - if (exists.size()) exists.attr("data-state", stateNew).attr("fill", color).attr("stroke", color); - else temp.append("polygon").attr("data-cell", i).attr("data-state", stateNew).attr("points", getPackPolygon(i)).attr("fill", color).attr("stroke", color); + if (exists.size()) exists.attr('data-state', stateNew).attr('fill', color).attr('stroke', color); + else temp.append('polygon').attr('data-cell', i).attr('data-state', stateNew).attr('points', getPackPolygon(i)).attr('fill', color).attr('stroke', color); }); } @@ -710,33 +806,42 @@ function editStates() { } function applyStatesManualAssignent() { - const cells = pack.cells, affectedStates = [], affectedProvinces = []; + const cells = pack.cells, + affectedStates = [], + affectedProvinces = []; - statesBody.select("#temp").selectAll("polygon").each(function() { - const i = +this.dataset.cell; - const c = +this.dataset.state; - affectedStates.push(cells.state[i], c); - affectedProvinces.push(cells.province[i]); - cells.state[i] = c; - if (cells.burg[i]) pack.burgs[cells.burg[i]].state = c; - }); + statesBody + .select('#temp') + .selectAll('polygon') + .each(function () { + const i = +this.dataset.cell; + const c = +this.dataset.state; + affectedStates.push(cells.state[i], c); + affectedProvinces.push(cells.province[i]); + cells.state[i] = c; + if (cells.burg[i]) pack.burgs[cells.burg[i]].state = c; + }); if (affectedStates.length) { refreshStatesEditor(); - if (!layerIsOn("toggleStates")) toggleStates(); else drawStates(); + if (!layerIsOn('toggleStates')) toggleStates(); + else drawStates(); if (adjustLabels.checked) BurgsAndStates.drawStateLabels([...new Set(affectedStates)]); adjustProvinces([...new Set(affectedProvinces)]); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); - if (layerIsOn("toggleProvinces")) drawProvinces(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); + if (layerIsOn('toggleProvinces')) drawProvinces(); } exitStatesManualAssignment(); } function adjustProvinces(affectedProvinces) { - const cells = pack.cells, provinces = pack.provinces, states = pack.states; - const form = {"Zone":1, "Area":1, "Territory":2, "Province":1}; + const cells = pack.cells, + provinces = pack.provinces, + states = pack.states; + const form = {Zone: 1, Area: 1, Territory: 2, Province: 1}; - affectedProvinces.forEach(p => { + affectedProvinces.forEach((p) => { // do nothing if neutral lands are captured if (!p) return; @@ -745,8 +850,8 @@ function editStates() { if (states[old].provinces.includes(p)) states[old].provinces.splice(states[old].provinces.indexOf(p), 1); // find states owning at least 1 province cell - const provCells = cells.i.filter(i => cells.province[i] === p); - const provStates = [...new Set(provCells.map(i => cells.state[i]))]; + const provCells = cells.i.filter((i) => cells.province[i] === p); + const provStates = [...new Set(provCells.map((i) => cells.state[i]))]; // assign province to its center owner; if center is neutral, remove province const owner = cells.state[provinces[p].center]; @@ -754,10 +859,10 @@ function editStates() { const name = provinces[p].name; // if province is historical part of abouther state province, unite with old province - const part = states[owner].provinces.find(n => name.includes(provinces[n].name)); + const part = states[owner].provinces.find((n) => name.includes(provinces[n].name)); if (part) { provinces[p].removed = true; - provCells.filter(i => cells.state[i] === owner).forEach(i => cells.province[i] = part); + provCells.filter((i) => cells.state[i] === owner).forEach((i) => (cells.province[i] = part)); } else { provinces[p].state = owner; states[owner].provinces.push(p); @@ -765,66 +870,83 @@ function editStates() { } } else { provinces[p].removed = true; - provCells.filter(i => !cells.state[i]).forEach(i => cells.province[i] = 0); + provCells.filter((i) => !cells.state[i]).forEach((i) => (cells.province[i] = 0)); } // create new provinces for non-main part - provStates.filter(s => s && s !== owner).forEach(s => createProvince(p, s, provCells.filter(i => cells.state[i] === s))); + provStates + .filter((s) => s && s !== owner) + .forEach((s) => + createProvince( + p, + s, + provCells.filter((i) => cells.state[i] === s) + ) + ); }); function createProvince(initProv, state, provCells) { const province = provinces.length; - provCells.forEach(i => cells.province[i] = province); + provCells.forEach((i) => (cells.province[i] = province)); - const burgCell = provCells.find(i => cells.burg[i]); + const burgCell = provCells.find((i) => cells.burg[i]); const center = burgCell ? burgCell : provCells[0]; const burg = burgCell ? cells.burg[burgCell] : 0; - const name = burgCell && P(.7) ? getAdjective(pack.burgs[burg].name) - : getAdjective(states[state].name) + " " + provinces[initProv].name.split(" ").slice(-1)[0]; - const formName = name.split(" ").length > 1 ? provinces[initProv].formName : rw(form); - const fullName = name + " " + formName; + const name = burgCell && P(0.7) ? getAdjective(pack.burgs[burg].name) : getAdjective(states[state].name) + ' ' + provinces[initProv].name.split(' ').slice(-1)[0]; + const formName = name.split(' ').length > 1 ? provinces[initProv].formName : rw(form); + const fullName = name + ' ' + formName; const color = getMixedColor(states[state].color); - provinces.push({i:province, state, center, burg, name, formName, fullName, color}); + provinces.push({i: province, state, center, burg, name, formName, fullName, color}); } - } - + function exitStatesManualAssignment(close) { customization = 0; - statesBody.select("#temp").remove(); + statesBody.select('#temp').remove(); removeCircle(); - document.querySelectorAll("#statesBottom > button").forEach(el => el.style.display = "inline-block"); - document.getElementById("statesManuallyButtons").style.display = "none"; - document.getElementById("statesHalo").style.display = "block"; + document.querySelectorAll('#statesBottom > button').forEach((el) => (el.style.display = 'inline-block')); + document.getElementById('statesManuallyButtons').style.display = 'none'; + document.getElementById('statesHalo').style.display = 'block'; - statesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden")); - statesFooter.style.display = "block"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if(!close) $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); + statesEditor.querySelectorAll('.hide:not(.show)').forEach((el) => el.classList.remove('hidden')); + statesFooter.style.display = 'block'; + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (!close) $('#statesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); restoreDefaultEvents(); clearMainTip(); - const selected = body.querySelector("div.selected"); - if (selected) selected.classList.remove("selected"); + const selected = body.querySelector('div.selected'); + if (selected) selected.classList.remove('selected'); } function enterAddStateMode() { - if (this.classList.contains("pressed")) {exitAddStateMode(); return;}; + if (this.classList.contains('pressed')) { + exitAddStateMode(); + return; + } customization = 3; - this.classList.add("pressed"); - tip("Click on the map to create a new capital or promote an existing burg", true); - viewbox.style("cursor", "crosshair").on("click", addState); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "none"); + this.classList.add('pressed'); + tip('Click on the map to create a new capital or promote an existing burg', true); + viewbox.style('cursor', 'crosshair').on('click', addState); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'none')); } function addState() { - const states = pack.states, burgs = pack.burgs, cells = pack.cells; + const states = pack.states, + burgs = pack.burgs, + cells = pack.cells; const point = d3.mouse(this); const center = findCell(point[0], point[1]); - if (cells.h[center] < 20) {tip("You cannot place state into the water. Please click on a land cell", false, "error"); return;} + if (cells.h[center] < 20) { + tip('You cannot place state into the water. Please click on a land cell', false, 'error'); + return; + } let burg = cells.burg[center]; - if (burg && burgs[burg].capital) {tip("Existing capital cannot be selected as a new state capital! Select other cell", false, "error"); return;} + if (burg && burgs[burg].capital) { + tip('Existing capital cannot be selected as a new state capital! Select other cell', false, 'error'); + return; + } if (!burg) burg = addBurg(point); // add new burg const oldState = cells.state[center]; @@ -833,64 +955,78 @@ function editStates() { // turn burg into a capital burgs[burg].capital = 1; burgs[burg].state = newState; - moveBurgToGroup(burg, "cities"); + moveBurgToGroup(burg, 'cities'); if (d3.event.shiftKey === false) exitAddStateMode(); const culture = cells.culture[center]; - const basename = center%5 === 0 ? burgs[burg].name : Names.getCulture(culture); + const basename = center % 5 === 0 ? burgs[burg].name : Names.getCulture(culture); const name = Names.getState(basename, culture); const color = getRandomColor(); const pole = cells.p[center]; // generate emblem const cultureType = pack.cultures[culture].type; - const coa = COA.generate(burgs[burg].coa, .4, null, cultureType); + const coa = COA.generate(burgs[burg].coa, 0.4, null, cultureType); coa.shield = COA.getShield(culture, null); // update diplomacy and reverse relations - const diplomacy = states.map(s => { - if (!s.i || s.removed) return "x"; + const diplomacy = states.map((s) => { + if (!s.i || s.removed) return 'x'; if (!oldState) { - s.diplomacy.push("Neutral"); - return "Neutral"; + s.diplomacy.push('Neutral'); + return 'Neutral'; } let relations = states[oldState].diplomacy[s.i]; // relations between Nth state and old overlord - if (s.i === oldState) relations = "Enemy"; // new state is Enemy to its old overlord - else if (relations === "Ally") relations = "Suspicion"; - else if (relations === "Friendly") relations = "Suspicion"; - else if (relations === "Suspicion") relations = "Neutral"; - else if (relations === "Enemy") relations = "Friendly"; - else if (relations === "Rival") relations = "Friendly"; - else if (relations === "Vassal") relations = "Suspicion"; - else if (relations === "Suzerain") relations = "Enemy"; + if (s.i === oldState) relations = 'Enemy'; + // new state is Enemy to its old overlord + else if (relations === 'Ally') relations = 'Suspicion'; + else if (relations === 'Friendly') relations = 'Suspicion'; + else if (relations === 'Suspicion') relations = 'Neutral'; + else if (relations === 'Enemy') relations = 'Friendly'; + else if (relations === 'Rival') relations = 'Friendly'; + else if (relations === 'Vassal') relations = 'Suspicion'; + else if (relations === 'Suzerain') relations = 'Enemy'; s.diplomacy.push(relations); return relations; }); - diplomacy.push("x"); + diplomacy.push('x'); states[0].diplomacy.push([`Independance declaration`, `${name} declared its independance from ${states[oldState].name}`]); cells.state[center] = newState; cells.province[center] = 0; - states.push({i:newState, name, diplomacy, provinces:[], color, expansionism:.5, capital:burg, type:"Generic", center, culture, military:[], alert:1, coa, pole}); + states.push({i: newState, name, diplomacy, provinces: [], color, expansionism: 0.5, capital: burg, type: 'Generic', center, culture, military: [], alert: 1, coa, pole}); BurgsAndStates.collectStatistics(); BurgsAndStates.defineStateForms([newState]); adjustProvinces([cells.province[center]]); - if (layerIsOn("toggleProvinces")) toggleProvinces(); - if (!layerIsOn("toggleStates")) toggleStates(); else drawStates(); - if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders(); + if (layerIsOn('toggleProvinces')) toggleProvinces(); + if (!layerIsOn('toggleStates')) toggleStates(); + else drawStates(); + if (!layerIsOn('toggleBorders')) toggleBorders(); + else drawBorders(); // add label - defs.select("#textPaths").append("path").attr("d", `M${pole[0]-50},${pole[1]+6}h${100}`).attr("id", "textPath_stateLabel"+newState); - labels.select("#states") - .append("text").attr("id", "stateLabel"+newState) - .append("textPath").attr("xlink:href", "#textPath_stateLabel"+newState).attr("startOffset", "50%").attr("font-size", "50%") - .append("tspan").attr("x", name.length * -3).text(name); + defs + .select('#textPaths') + .append('path') + .attr('d', `M${pole[0] - 50},${pole[1] + 6}h${100}`) + .attr('id', 'textPath_stateLabel' + newState); + labels + .select('#states') + .append('text') + .attr('id', 'stateLabel' + newState) + .append('textPath') + .attr('xlink:href', '#textPath_stateLabel' + newState) + .attr('startOffset', '50%') + .attr('font-size', '50%') + .append('tspan') + .attr('x', name.length * -3) + .text(name); - COArenderer.add("state", newState, coa, states[newState].pole[0], states[newState].pole[1]); + COArenderer.add('state', newState, coa, states[newState].pole[0], states[newState].pole[1]); statesEditorAddLines(); } @@ -898,40 +1034,40 @@ function editStates() { customization = 0; restoreDefaultEvents(); clearMainTip(); - body.querySelectorAll("div > input, select, span, svg").forEach(e => e.style.pointerEvents = "all"); - if (statesAdd.classList.contains("pressed")) statesAdd.classList.remove("pressed"); + body.querySelectorAll('div > input, select, span, svg').forEach((e) => (e.style.pointerEvents = 'all')); + if (statesAdd.classList.contains('pressed')) statesAdd.classList.remove('pressed'); } function downloadStatesData() { - const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value; - let data = "Id,State,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area "+unit+",Total Population,Rural Population,Urban Population\n"; // headers + const unit = areaUnit.value === 'square' ? distanceUnitInput.value + '2' : areaUnit.value; + let data = 'Id,State,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area ' + unit + ',Total Population,Rural Population,Urban Population\n'; // headers - body.querySelectorAll(":scope > div").forEach(function(el) { + body.querySelectorAll(':scope > div').forEach(function (el) { const key = parseInt(el.dataset.id); - data += el.dataset.id + ","; - data += el.dataset.name + ","; - data += el.dataset.form + ","; - data += el.dataset.color + ","; - data += el.dataset.capital + ","; - data += el.dataset.culture + ","; - data += el.dataset.type + ","; - data += el.dataset.expansionism + ","; - data += el.dataset.cells + ","; - data += el.dataset.burgs + ","; - data += el.dataset.area + ","; - data += el.dataset.population + ","; - data += `${Math.round(pack.states[key].rural*populationRate.value)},`; - data += `${Math.round(pack.states[key].urban*populationRate.value * urbanization.value)}\n`; + data += el.dataset.id + ','; + data += el.dataset.name + ','; + data += el.dataset.form + ','; + data += el.dataset.color + ','; + data += el.dataset.capital + ','; + data += el.dataset.culture + ','; + data += el.dataset.type + ','; + data += el.dataset.expansionism + ','; + data += el.dataset.cells + ','; + data += el.dataset.burgs + ','; + data += el.dataset.area + ','; + data += el.dataset.population + ','; + data += `${Math.round(pack.states[key].rural * populationRate.value)},`; + data += `${Math.round(pack.states[key].urban * populationRate.value * urbanization.value)}\n`; }); - const name = getFileName("States") + ".csv"; + const name = getFileName('States') + '.csv'; downloadFile(data, name); } function closeStatesEditor() { - if (customization === 2) exitStatesManualAssignment("close"); + if (customization === 2) exitStatesManualAssignment('close'); if (customization === 3) exitAddStateMode(); - debug.selectAll(".highlight").remove(); - body.innerHTML = ""; + debug.selectAll('.highlight').remove(); + body.innerHTML = ''; } } diff --git a/modules/ui/style.js b/modules/ui/style.js index 9386efe4..882cc8ff 100644 --- a/modules/ui/style.js +++ b/modules/ui/style.js @@ -1,8 +1,8 @@ // UI module to control the style -"use strict"; +'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); }); @@ -14,334 +14,347 @@ function editStyle(element, group) { if (group) styleGroupSelect.options.add(new Option(group, group, true, true)); selectStyleElement(); - styleElementSelect.classList.add("glow"); - if (group) styleGroupSelect.classList.add("glow"); + styleElementSelect.classList.add('glow'); + if (group) styleGroupSelect.classList.add('glow'); setTimeout(() => { - styleElementSelect.classList.remove("glow"); - if (group) styleGroupSelect.classList.remove("glow"); + styleElementSelect.classList.remove('glow'); + if (group) styleGroupSelect.classList.remove('glow'); }, 1500); } // Toggle style sections on element select -styleElementSelect.addEventListener("change", selectStyleElement); +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)) { - el = d3.select("#"+sel).select("g#"+group).size() - ? d3.select("#"+sel).select("g#"+group) - : d3.select("#"+sel).select("g"); + if (['routes', 'labels', 'coastline', 'lakes', 'anchors', 'burgIcons', 'borders'].includes(sel)) { + el = d3 + .select('#' + sel) + .select('g#' + group) + .size() + ? d3.select('#' + sel).select('g#' + group) + : d3.select('#' + sel).select('g'); } - if (sel !== "landmass" && sel !== "legend") { + if (sel !== 'landmass' && sel !== 'legend') { // opacity - if (sel !== "ocean") { - styleOpacity.style.display = "block"; - styleOpacityInput.value = styleOpacityOutput.value = el.attr("opacity") || 1; + if (sel !== 'ocean') { + styleOpacity.style.display = 'block'; + styleOpacityInput.value = styleOpacityOutput.value = el.attr('opacity') || 1; } // filter - styleFilter.style.display = "block"; - styleFilterInput.value = el.attr("filter") || ""; + styleFilter.style.display = 'block'; + styleFilterInput.value = el.attr('filter') || ''; } // fill - if (["rivers", "lakes", "landmass", "prec", "ice", "fogging"].includes(sel)) { - styleFill.style.display = "block"; - styleFillInput.value = styleFillOutput.value = el.attr("fill"); + if (['rivers', 'lakes', 'landmass', 'prec', 'ice', 'fogging'].includes(sel)) { + styleFill.style.display = 'block'; + styleFillInput.value = styleFillOutput.value = el.attr('fill'); } // stroke color and width - if (["armies", "routes", "lakes", "borders", "cults", "relig", "cells", "coastline", "prec", "ice", "icons", "coordinates", "zones", "gridOverlay"].includes(sel)) { - styleStroke.style.display = "block"; - styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke"); - styleStrokeWidth.style.display = "block"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || ""; + if (['armies', 'routes', 'lakes', 'borders', 'cults', 'relig', 'cells', 'coastline', 'prec', 'ice', 'icons', 'coordinates', 'zones', 'gridOverlay'].includes(sel)) { + styleStroke.style.display = 'block'; + styleStrokeInput.value = styleStrokeOutput.value = el.attr('stroke'); + styleStrokeWidth.style.display = 'block'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || ''; } // stroke dash - if (["routes", "borders", "temperature", "legend", "population", "coordinates", "zones", "gridOverlay"].includes(sel)) { - styleStrokeDash.style.display = "block"; - styleStrokeDasharrayInput.value = el.attr("stroke-dasharray") || ""; - styleStrokeLinecapInput.value = el.attr("stroke-linecap") || "inherit"; + if (['routes', 'borders', 'temperature', 'legend', 'population', 'coordinates', 'zones', 'gridOverlay'].includes(sel)) { + styleStrokeDash.style.display = 'block'; + styleStrokeDasharrayInput.value = el.attr('stroke-dasharray') || ''; + styleStrokeLinecapInput.value = el.attr('stroke-linecap') || 'inherit'; } // clipping - if (["cells", "gridOverlay", "coordinates", "compass", "terrain", "temperature", "routes", "texture", "biomes", "zones"].includes(sel)) { - styleClipping.style.display = "block"; - styleClippingInput.value = el.attr("mask") || ""; + if (['cells', 'gridOverlay', 'coordinates', 'compass', 'terrain', 'temperature', 'routes', 'texture', 'biomes', 'zones'].includes(sel)) { + styleClipping.style.display = 'block'; + styleClippingInput.value = el.attr('mask') || ''; } // 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"); - styleHeightmapTerracing.value = styleHeightmapTerracingOutput.value = terrs.attr("terracing"); - styleHeightmapSkip.value = styleHeightmapSkipOutput.value = terrs.attr("skip"); - styleHeightmapSimplification.value = styleHeightmapSimplificationOutput.value = terrs.attr("relax"); - styleHeightmapCurve.value = terrs.attr("curve"); + if (sel === 'terrs') { + styleHeightmap.style.display = 'block'; + styleHeightmapScheme.value = terrs.attr('scheme'); + styleHeightmapTerracing.value = styleHeightmapTerracingOutput.value = terrs.attr('terracing'); + styleHeightmapSkip.value = styleHeightmapSkipOutput.value = terrs.attr('skip'); + styleHeightmapSimplification.value = styleHeightmapSimplificationOutput.value = terrs.attr('relax'); + styleHeightmapCurve.value = terrs.attr('curve'); } - if (sel === "markers") { - styleMarkers.style.display = "block"; - styleRescaleMarkers.checked = +markers.attr("rescale"); + if (sel === 'markers') { + styleMarkers.style.display = 'block'; + styleRescaleMarkers.checked = +markers.attr('rescale'); } - if (sel === "gridOverlay") { - styleGrid.style.display = "block"; - styleGridType.value = el.attr("type"); - styleGridScale.value = el.attr("scale") || 1; - styleGridShiftX.value = el.attr("dx") || 0; - styleGridShiftY.value = el.attr("dy") || 0; + if (sel === 'gridOverlay') { + styleGrid.style.display = 'block'; + styleGridType.value = el.attr('type'); + styleGridScale.value = el.attr('scale') || 1; + styleGridShiftX.value = el.attr('dx') || 0; + styleGridShiftY.value = el.attr('dy') || 0; calculateFriendlyGridSize(); } - if (sel === "compass") { - styleCompass.style.display = "block"; - const tr = parseTransform(compass.select("use").attr("transform")); + if (sel === 'compass') { + styleCompass.style.display = 'block'; + const tr = parseTransform(compass.select('use').attr('transform')); styleCompassShiftX.value = tr[0]; styleCompassShiftY.value = tr[1]; styleCompassSizeInput.value = styleCompassSizeOutput.value = tr[2]; } - if (sel === "terrain") { - styleRelief.style.display = "block"; - styleReliefSizeOutput.innerHTML = styleReliefSizeInput.value = terrain.attr("size"); - styleReliefDensityOutput.innerHTML = styleReliefDensityInput.value = terrain.attr("density"); - styleReliefSet.value = terrain.attr("set"); + if (sel === 'terrain') { + styleRelief.style.display = 'block'; + styleReliefSizeOutput.innerHTML = styleReliefSizeInput.value = terrain.attr('size'); + styleReliefDensityOutput.innerHTML = styleReliefDensityInput.value = terrain.attr('density'); + styleReliefSet.value = terrain.attr('set'); } - if (sel === "population") { - stylePopulation.style.display = "block"; - stylePopulationRuralStrokeInput.value = stylePopulationRuralStrokeOutput.value = population.select("#rural").attr("stroke"); - stylePopulationUrbanStrokeInput.value = stylePopulationUrbanStrokeOutput.value = population.select("#urban").attr("stroke"); - styleStrokeWidth.style.display = "block"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || ""; + if (sel === 'population') { + stylePopulation.style.display = 'block'; + stylePopulationRuralStrokeInput.value = stylePopulationRuralStrokeOutput.value = population.select('#rural').attr('stroke'); + stylePopulationUrbanStrokeInput.value = stylePopulationUrbanStrokeOutput.value = population.select('#urban').attr('stroke'); + styleStrokeWidth.style.display = 'block'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || ''; } - if (sel === "regions") { - styleStates.style.display = "block"; - styleStatesHaloWidth.value = styleStatesHaloWidthOutput.value = statesHalo.attr("data-width"); - styleStatesHaloOpacity.value = styleStatesHaloOpacityOutput.value = statesHalo.attr("opacity"); + if (sel === 'regions') { + styleStates.style.display = 'block'; + styleStatesHaloWidth.value = styleStatesHaloWidthOutput.value = statesHalo.attr('data-width'); + styleStatesHaloOpacity.value = styleStatesHaloOpacityOutput.value = statesHalo.attr('opacity'); } - if (sel === "labels") { - styleFill.style.display = "block"; - styleStroke.style.display = "block"; - styleStrokeWidth.style.display = "block"; + if (sel === 'labels') { + styleFill.style.display = 'block'; + styleStroke.style.display = 'block'; + styleStrokeWidth.style.display = 'block'; loadDefaultFonts(); - styleFont.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; - styleSelectFont.value = fonts.indexOf(el.attr("data-font")); - styleInputFont.style.display = "none"; - styleInputFont.value = ""; - styleFontSize.value = el.attr("data-size"); + styleFont.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; + styleSelectFont.value = fonts.indexOf(el.attr('data-font')); + styleInputFont.style.display = 'none'; + styleInputFont.value = ''; + styleFontSize.value = el.attr('data-size'); } - if (sel === "provs") { - styleFill.style.display = "block"; + if (sel === 'provs') { + styleFill.style.display = 'block'; loadDefaultFonts(); - styleFont.style.display = "block"; - styleSize.style.display = "block"; - styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#111111"; - styleSelectFont.value = fonts.indexOf(el.attr("data-font")); - styleInputFont.style.display = "none"; - styleInputFont.value = ""; - styleFontSize.value = el.attr("data-size"); + styleFont.style.display = 'block'; + styleSize.style.display = 'block'; + styleFillInput.value = styleFillOutput.value = el.attr('fill') || '#111111'; + styleSelectFont.value = fonts.indexOf(el.attr('data-font')); + styleInputFont.style.display = 'none'; + styleInputFont.value = ''; + 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") || .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") || .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"; + 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") || .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("oceanPattern").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('oceanPattern').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") || .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") { - styleSize.style.display = "block"; - styleFontSize.value = el.attr("data-size"); + if (sel === 'coordinates') { + styleSize.style.display = 'block'; + styleFontSize.value = el.attr('data-size'); } - if (sel === "armies") { - styleArmies.style.display = "block"; - styleArmiesFillOpacity.value = styleArmiesFillOpacityOutput.value = el.attr("fill-opacity"); - styleArmiesSize.value = styleArmiesSizeOutput.value = el.attr("box-size"); + if (sel === 'armies') { + styleArmies.style.display = 'block'; + styleArmiesFillOpacity.value = styleArmiesFillOpacityOutput.value = el.attr('fill-opacity'); + styleArmiesSize.value = styleArmiesSizeOutput.value = el.attr('box-size'); } - if (sel === "emblems") { - styleEmblems.style.display = "block"; - styleStrokeWidth.style.display = "block"; - styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || 1; + if (sel === 'emblems') { + styleEmblems.style.display = 'block'; + styleStrokeWidth.style.display = 'block'; + styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr('stroke-width') || 1; } // update group options styleGroupSelect.options.length = 0; // remove all options - if (["routes", "labels", "coastline", "lakes", "anchors", "burgIcons", "borders"].includes(sel)) { - document.getElementById(sel).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"); + if (['routes', 'labels', 'coastline', 'lakes', 'anchors', 'burgIcons', 'borders'].includes(sel)) { + document + .getElementById(sel) + .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'); } 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'; } - } // Handle style inputs change -styleGroupSelect.addEventListener("change", selectStyleElement); +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); + const el = styleElementSelect.value, + g = styleGroupSelect.value; + 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); }); -styleStrokeInput.addEventListener("input", function() { +styleStrokeInput.addEventListener('input', function () { styleStrokeOutput.value = this.value; getEl().attr('stroke', this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); + 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(); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleStrokeDasharrayInput.addEventListener("input", function() { +styleStrokeDasharrayInput.addEventListener('input', function () { getEl().attr('stroke-dasharray', this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleStrokeLinecapInput.addEventListener("change", function() { +styleStrokeLinecapInput.addEventListener('change', function () { getEl().attr('stroke-linecap', this.value); - if (styleElementSelect.value === "gridOverlay" && layerIsOn("toggleGrid")) drawGrid(); + if (styleElementSelect.value === 'gridOverlay' && layerIsOn('toggleGrid')) drawGrid(); }); -styleOpacityInput.addEventListener("input", function() { +styleOpacityInput.addEventListener('input', function () { styleOpacityOutput.value = this.value; getEl().attr('opacity', this.value); }); -styleFilterInput.addEventListener("change", function() { - if (styleGroupSelect.value === "ocean") { +styleFilterInput.addEventListener('change', function () { + if (styleGroupSelect.value === 'ocean') { oceanLayers.attr('filter', this.value); return; } 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() { - texture.select("image").attr("x", this.value).attr("width", graphWidth - this.valueAsNumber); +styleTextureShiftX.addEventListener('input', function () { + texture + .select('image') + .attr('x', this.value) + .attr('width', graphWidth - this.valueAsNumber); }); -styleTextureShiftY.addEventListener("input", function() { - texture.select("image").attr("y", this.value).attr("height", graphHeight - this.valueAsNumber); +styleTextureShiftY.addEventListener('input', function () { + texture + .select('image') + .attr('y', this.value) + .attr('height', graphHeight - this.valueAsNumber); }); -styleClippingInput.addEventListener("change", function() { +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(); }); @@ -351,336 +364,354 @@ 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); -styleShiftY.addEventListener("input", shiftElement); +styleShiftX.addEventListener('input', shiftElement); +styleShiftY.addEventListener('input', shiftElement); function shiftElement() { const x = styleShiftX.value || 0; const y = styleShiftY.value || 0; - getEl().attr("transform", `translate(${x},${y})`); + 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("oceanPattern").setAttribute("opacity", this.value); +styleOceanPatternOpacity.addEventListener('input', function () { + document.getElementById('oceanPattern').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(); }); -styleHeightmapTerracing.addEventListener("input", function() { +styleHeightmapTerracing.addEventListener('input', function () { styleHeightmapTerracingOutput.value = this.value; - terrs.attr("terracing", this.value); + terrs.attr('terracing', this.value); drawHeightmap(); }); -styleHeightmapSkip.addEventListener("input", function() { +styleHeightmapSkip.addEventListener('input', function () { styleHeightmapSkipOutput.value = this.value; - terrs.attr("skip", this.value); + terrs.attr('skip', this.value); drawHeightmap(); }); -styleHeightmapSimplification.addEventListener("input", function() { +styleHeightmapSimplification.addEventListener('input', function () { styleHeightmapSimplificationOutput.value = this.value; - terrs.attr("relax", this.value); + 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(); + 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(); + 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(); }); -styleCompassShiftX.addEventListener("input", shiftCompass); -styleCompassShiftY.addEventListener("input", shiftCompass); +styleCompassShiftX.addEventListener('input', shiftCompass); +styleCompassShiftY.addEventListener('input', shiftCompass); function shiftCompass() { const tr = `translate(${styleCompassShiftX.value} ${styleCompassShiftY.value}) scale(${styleCompassSizeInput.value})`; - compass.select("use").attr("transform", tr); + 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); + 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); + 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); + legend.select('#legendBox').attr('fill-opacity', this.value); }); -styleSelectFont.addEventListener("change", changeFont); +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(); } -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"; + styleSelectFont.style.display = 'none'; } else { - styleInputFont.style.display = "none"; - styleSelectFont.style.display = "inline-block"; + styleInputFont.style.display = 'none'; + styleSelectFont.style.display = 'inline-block'; } }); -styleInputFont.addEventListener("change", function() { - if (!this.value) {tip("Please provide a valid Google font name or link to a @font-face declaration"); return;} - fetchFonts(this.value).then(fetched => { +styleInputFont.addEventListener('change', function () { + if (!this.value) { + tip('Please provide a valid Google font name or link to a @font-face declaration'); + return; + } + fetchFonts(this.value).then((fetched) => { if (!fetched) return; styleFontAdd.click(); - styleInputFont.value = ""; + styleInputFont.value = ''; if (fetched !== 1) return; - styleSelectFont.value = fonts.length-1; + styleSelectFont.value = fonts.length - 1; changeFont(); // auto-change font if 1 font is fetched }); }); -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") * .9, 2), 1); +styleFontMinus.addEventListener('click', function () { + const size = Math.max(rn(getEl().attr('data-size') * 0.9, 2), 1); changeFontSize(size); }); function changeFontSize(size) { - const legend = styleElementSelect.value === "legend"; - const coords = styleElementSelect.value === "coordinates"; + const legend = styleElementSelect.value === 'legend'; + const coords = styleElementSelect.value === 'coordinates'; - const desSize = legend ? size : coords ? rn(size / scale ** .8, 2) : rn(size + (size / scale)); - getEl().attr("data-size", size).attr("font-size", desSize); + const desSize = legend ? size : coords ? rn(size / scale ** 0.8, 2) : rn(size + size / scale); + 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), .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") * .9, 2), .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").each(function() {this.setAttribute("dy", `${size * -1.5}px`)}); + burgLabels + .select('g#' + g) + .selectAll('text') + .each(function () { + 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), .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") * .9, 2), .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); + 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); + 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 => { + armies.selectAll('g').remove(); // clear armies layer + pack.states.forEach((s) => { if (!s.i || s.removed || !s.military.length) return; Military.drawRegiments(s.military, s.i); }); }); -styleEmblemsStateSizeInput.addEventListener("input", drawEmblems); -styleEmblemsProvinceSizeInput.addEventListener("input", drawEmblems); -styleEmblemsBurgSizeInput.addEventListener("input", drawEmblems); +styleEmblemsStateSizeInput.addEventListener('input', drawEmblems); +styleEmblemsProvinceSizeInput.addEventListener('input', drawEmblems); +styleEmblemsBurgSizeInput.addEventListener('input', drawEmblems); // request a URL to image to be used as a texture function textureProvideURL() { alertMessage.innerHTML = `Provide an image URL to be used as a texture: `; - $("#alert").dialog({resizable: false, title: "Load custom texture", width: "26em", + $('#alert').dialog({ + resizable: false, + 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"); return;} - const opt = document.createElement("option"); + Apply: function () { + const name = textureURL.value.split('/').pop(); + if (!name || name === '') { + tip('Please provide a valid URL', false, 'error'); + return; + } + const opt = document.createElement('option'); opt.value = textureURL.value; opt.text = name.slice(0, 20); styleTextureInput.add(opt); styleTextureInput.value = textureURL.value; - getBase64(textureURL.value, base64 => texture.select("image").attr("xlink:href", base64)); + getBase64(textureURL.value, (base64) => texture.select('image').attr('xlink:href', base64)); zoom.scaleBy(svg, 1.00001); // enforce browser re-draw - $(this).dialog("close"); + $(this).dialog('close'); }, - Cancel: function() {$(this).dialog("close");} + Cancel: function () { + $(this).dialog('close'); + } } }); } function fetchTextureURL(url) { - INFO && console.log("Provided URL is", url); + INFO && console.log('Provided URL is', url); const img = new Image(); img.onload = function () { - const canvas = document.getElementById("texturePreview"); - const ctx = canvas.getContext("2d"); + const canvas = document.getElementById('texturePreview'); + const ctx = canvas.getContext('2d'); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.drawImage(img, 0, 0, canvas.width, canvas.height); }; @@ -692,11 +723,11 @@ const defaultStyles = { styleGloom: `{"#map":{"background-color":"#000000","filter":null,"data-filter":null},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.3,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":null,"filter":"url(#blur5)","mask":"url(#land)"},"#stateBorders":{"opacity":1,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":""},"#provinceBorders":{"opacity":1,"stroke":"#56566d","stroke-width":0.3,"stroke-dasharray":".7 1","stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":14,"font-size":14,"stroke":"#4a4a4a","stroke-width":1,"stroke-dasharray":6,"stroke-linecap":null,"filter":"","mask":""},"#compass":{"opacity":0.6,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":"translate(100 100) scale(0.3)"},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":1,"filter":null},"#cults":{"opacity":0.7,"stroke":"#777777","stroke-width":1.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#e0e0e0","filter":null},"#markers":{"opacity":0.8,"rescale":1,"filter":"url(#dropShadow05)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000aa"},"#urban":{"stroke":"#9d0000"},"#freshwater":{"opacity":0.5,"fill":"#a6c1fd","stroke":"#5f799d","stroke-width":0.7,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.6,"stroke":"#1f3846","stroke-width":0.7,"filter":"url(#dropShadow)","auto-filter":1},"#lake_island":{"opacity":1,"stroke":"#7c8eaf","stroke-width":0.35,"filter":null},"#terrain":{"opacity":0.9,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":"","fill":"#779582"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":1,"stroke":"#8b4418","stroke-width":0.9,"stroke-dasharray":"2 3","stroke-linecap":"round","filter":"","mask":null},"#trails":{"opacity":1,"stroke":"#844017","stroke-width":0.2,"stroke-dasharray":".5 1","stroke-linecap":"round","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#5e1865","stroke-width":0.6,"stroke-dasharray":"1.2 2.4","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":"url(#dropShadow)"},"#statesHalo":{"opacity":1,"data-width":10.2,"stroke-width":10.2},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":1,"font-size":"11px","fill":"#62001b","fill-opacity":0.3,"stroke":null,"stroke-width":2,"stroke-dasharray":2,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow05)"},"#emblems": {"opacity":0.6,"stroke-width":0.5,"filter":null},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{"x":0,"y":0},"#zones":{"opacity":0.5,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":"url(#dropShadow01)","mask":null},"#ocean":{"opacity":1},"#oceanLayers":{"filter":null,"layers":"-6,-4,-2"},"#oceanBase":{"fill":"#4e6964"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":"./images/pattern3.jpg"},"#terrs":{"opacity":1,"scheme":"bright","terracing":0,"skip":0,"relax":1,"curve":1,"filter":"url(#filter-grayscale)","mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":7,"font-size":7,"data-font":"Bitter","font-family":"Bitter"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":2,"stroke":"#444444","stroke-width":0.25,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":0.8,"fill":"#ffffff","size":4,"stroke":"#3e3e4b","stroke-width":1},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":3,"font-size":3,"data-font":"Bitter","font-family":"Bitter"},"#burgIcons > #towns":{"opacity":0.95,"fill":"#ffffff","fill-opacity":0.7,"size":0.8,"stroke":"#3e3e4b","stroke-width":0.2,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1.6,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#4e4e4e","stroke":"#b5b5b5","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":""},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#1b1423","filter":null}}`, styleClean: `{"#map":{"background-color":"#000000","filter":null,"data-filter":null},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":0.5,"filter":"url(#blur7)","mask":"url(#land)"},"#stateBorders":{"opacity":0.8,"stroke":"#414141","stroke-width":0.7,"stroke-dasharray":0,"stroke-linecap":"butt","filter":""},"#provinceBorders":{"opacity":0.8,"stroke":"#414141","stroke-width":0.45,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.09,"filter":null,"mask":"url(#land)"},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#414141","stroke-width":0.45,"stroke-dasharray":3,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":null},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#eeedeb","filter":null},"#markers":{"opacity":null,"rescale":null,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0,"fill":"#0080ff","filter":null},"#population":{"opacity":null,"stroke-width":2.58,"stroke-dasharray":0,"stroke-linecap":"butt","filter":"url(#blur3)"},"#rural":{"stroke":"#ff0000"},"#urban":{"stroke":"#800000"},"#freshwater":{"opacity":0.5,"fill":"#aadaff","stroke":"#5f799d","stroke-width":0,"filter":null},"#salt":{"opacity":0.5,"fill":"#409b8a","stroke":"#388985","stroke-width":0.7,"filter":null},"#sinkhole":{"opacity":1,"fill":"#5bc9fd","stroke":"#53a3b0","stroke-width":0.7,"filter":null},"#frozen":{"opacity":0.95,"fill":"#cdd4e7","stroke":"#cfe0eb","stroke-width":0,"filter":null},"#lava":{"opacity":0.7,"fill":"#90270d","stroke":"#f93e0c","stroke-width":2,"filter":"url(#crumpled)"},"#dry":{"opacity":0.7,"fill":"#c9bfa7","stroke":"#8e816f","stroke-width":0.7,"filter":null},"#sea_island":{"opacity":0.6,"stroke":"#595959","stroke-width":0.4,"filter":"","auto-filter":0},"#lake_island":{"opacity":0,"stroke":"#7c8eaf","stroke-width":0,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":null,"filter":null,"fill":"#aadaff"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.9,"stroke":"#f6d068","stroke-width":0.7,"stroke-dasharray":0,"stroke-linecap":"inherit","filter":null,"mask":null},"#trails":{"opacity":1,"stroke":"#ffffff","stroke-width":0.25,"stroke-dasharray":"","stroke-linecap":"round","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#4f82c6","stroke-width":0.45,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null,"mask":"url(#water)"},"#regions":{"opacity":0.4,"filter":null},"#statesHalo":{"opacity":0,"data-width":null,"stroke-width":0},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow01)"},"#emblems":{"opacity":1,"stroke-width":1,"filter":null},"#texture":{"opacity":null,"filter":null,"mask":"url(#land)"},"#textureImage":{},"#zones":{"opacity":0.7,"stroke":"#ff6262","stroke-width":0,"stroke-dasharray":"","stroke-linecap":"butt","filter":null,"mask":null},"#ocean":{"opacity":null},"#oceanLayers":{"filter":"","layers":"none"},"#oceanBase":{"fill":"#aadaff"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":""},"#terrs":{"opacity":0.5,"scheme":"bright","terracing":0,"skip":5,"relax":0,"curve":0,"filter":"","mask":"url(#land)"},"#legend":{"data-size":12.74,"font-size":12.74,"data-font":"Arial","font-family":"Arial","stroke":"#909090","stroke-width":1.13,"stroke-dasharray":0,"stroke-linecap":"round","data-x":98.39,"data-y":12.67,"data-columns":null},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#414141","data-size":7,"font-size":7,"data-font":"Arial","font-family":"Arial"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":1,"stroke":"#3e3e4b","stroke-width":0.24,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#303030","stroke-width":1.7},"#burgLabels > #towns":{"opacity":1,"fill":"#414141","data-size":3,"font-size":3,"data-font":"Arial","font-family":"Arial"},"#burgIcons > #towns":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":0.5,"stroke":"#3e3e4b","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.06},"#labels > #states":{"opacity":1,"fill":"#292929","stroke":"#303030","stroke-width":0,"data-size":10,"font-size":10,"data-font":"Arial","font-family":"Arial","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#414141","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Arial","font-family":"Arial","filter":null},"#fogging":{"opacity":1,"fill":"#ffffff","filter":null}}`, styleMonochrome: `{"#map":{"background-color":"#000000","filter":"url(#filter-grayscale)","data-filter":"grayscale"},"#armies":{"font-size":6,"box-size":3,"stroke":"#000","stroke-width":0.3,"opacity":1,"fill-opacity":1,"filter":null},"#biomes":{"opacity":null,"filter":"url(#blur5)","mask":null},"#stateBorders":{"opacity":1,"stroke":"#56566d","stroke-width":1,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null},"#provinceBorders":{"opacity":1,"stroke":"#56566d","stroke-width":0.4,"stroke-dasharray":1,"stroke-linecap":"butt","filter":null},"#cells":{"opacity":null,"stroke":"#808080","stroke-width":0.1,"filter":null,"mask":null},"#gridOverlay":{"opacity":0.8,"scale":1,"dx":0,"dy":"0","type":"pointyHex","stroke":"#808080","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"transform":null,"filter":null,"mask":null},"#coordinates":{"opacity":1,"data-size":12,"font-size":12,"stroke":"#d4d4d4","stroke-width":1,"stroke-dasharray":5,"stroke-linecap":null,"filter":null,"mask":null},"#compass":{"opacity":0.8,"transform":null,"filter":null,"mask":"url(#water)","shape-rendering":"optimizespeed"},"#rose":{"transform":null},"#relig":{"opacity":0.7,"stroke":"#404040","stroke-width":0.7,"filter":null},"#cults":{"opacity":0.6,"stroke":"#777777","stroke-width":0.5,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#landmass":{"opacity":1,"fill":"#000000","filter":null},"#markers":{"opacity":null,"rescale":1,"filter":"url(#dropShadow01)"},"#prec":{"opacity":null,"stroke":"#000000","stroke-width":0.1,"fill":"#003dff","filter":null},"#population":{"opacity":null,"stroke-width":1.6,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null},"#rural":{"stroke":"#0000ff"},"#urban":{"stroke":"#ff0000"},"#freshwater":{"opacity":1,"fill":"#000000","stroke":"#515151","stroke-width":0,"filter":null},"#salt":{"opacity":1,"fill":"#000000","stroke":"#484848","stroke-width":0,"filter":null},"#sinkhole":{"opacity":1,"fill":"#000000","stroke":"#5f5f5f","stroke-width":0.5,"filter":null},"#frozen":{"opacity":1,"fill":"#000000","stroke":"#6f6f6f","stroke-width":0,"filter":null},"#lava":{"opacity":1,"fill":"#000000","stroke":"#5d5d5d","stroke-width":0,"filter":""},"#sea_island":{"opacity":1,"stroke":"#1f3846","stroke-width":0,"filter":"","auto-filter":0},"#lake_island":{"opacity":0,"stroke":"#7c8eaf","stroke-width":0,"filter":null},"#terrain":{"opacity":null,"set":"simple","size":1,"density":0.4,"filter":null,"mask":null},"#rivers":{"opacity":0.2,"filter":"url(#blur1)","fill":"#000000"},"#ruler":{"opacity":null,"filter":null},"#roads":{"opacity":0.9,"stroke":"#d06324","stroke-width":0.7,"stroke-dasharray":2,"stroke-linecap":"butt","filter":null,"mask":null},"#trails":{"opacity":0.9,"stroke":"#d06324","stroke-width":0.25,"stroke-dasharray":".8 1.6","stroke-linecap":"butt","filter":null,"mask":null},"#searoutes":{"opacity":0.8,"stroke":"#ffffff","stroke-width":0.45,"stroke-dasharray":"1 2","stroke-linecap":"round","filter":null,"mask":null},"#regions":{"opacity":0.4,"filter":null},"#statesHalo":{"opacity":1,"data-width":10,"stroke-width":10},"#provs":{"opacity":0.7,"fill":"#000000","data-size":10,"font-size":10,"font-family":"Georgia","data-font":"Georgia","filter":null},"#temperature":{"opacity":null,"font-size":"8px","fill":"#000000","fill-opacity":0.3,"stroke":null,"stroke-width":1.8,"stroke-dasharray":null,"stroke-linecap":null,"filter":null},"#ice":{"opacity":0.9,"fill":"#e8f0f6","stroke":"#e8f0f6","stroke-width":1,"filter":"url(#dropShadow05)"},"#texture":{"opacity":1,"filter":null,"mask":"url(#land)"},"#emblems": {"opacity": 0.5,"stroke-width": 0.5,"filter": null},"#textureImage":{},"#zones":{"opacity":0.6,"stroke":"#333333","stroke-width":0,"stroke-dasharray":null,"stroke-linecap":"butt","filter":null,"mask":null},"#ocean":{"opacity":0},"#oceanLayers":{"filter":null,"layers":"none"},"#oceanBase":{"fill":"#000000"},"#oceanPattern":{"opacity":0.2},"#oceanicPattern":{"href":""},"#terrs":{"opacity":1,"scheme":"monochrome","terracing":0,"skip":5,"relax":0,"curve":0,"filter":"url(#blur3)","mask":"url(#land)"},"#legend":{"data-size":13,"font-size":13,"data-font":"Almendra+SC","font-family":"Almendra SC","stroke":"#812929","stroke-width":2.5,"stroke-dasharray":"0 4 10 4","stroke-linecap":"round","data-x":99,"data-y":93,"data-columns":8},"#legendBox":{},"#burgLabels > #cities":{"opacity":1,"fill":"#3e3e4b","data-size":7,"font-size":7,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #cities":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":1,"stroke":"#3e3e4b","stroke-width":0.24,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #cities":{"opacity":1,"fill":"#ffffff","size":2,"stroke":"#3e3e4b","stroke-width":1.2},"#burgLabels > #towns":{"opacity":1,"fill":"#3e3e4b","data-size":4,"font-size":4,"data-font":"Almendra+SC","font-family":"Almendra SC"},"#burgIcons > #towns":{"opacity":1,"fill":"#ffffff","fill-opacity":0.7,"size":0.5,"stroke":"#3e3e4b","stroke-width":0.12,"stroke-dasharray":"","stroke-linecap":"butt"},"#anchors > #towns":{"opacity":1,"fill":"#ffffff","size":1,"stroke":"#3e3e4b","stroke-width":1.2},"#labels > #states":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":22,"font-size":22,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#labels > #addedLabels":{"opacity":1,"fill":"#3e3e4b","stroke":"#3a3a3a","stroke-width":0,"data-size":18,"font-size":18,"data-font":"Almendra+SC","font-family":"Almendra SC","filter":null},"#fogging":{"opacity":0.98,"fill":"#30426f","filter":null}}` -} +}; // apply default or custom style settings on load function applyStyleOnLoad() { - const preset = localStorage.getItem("presetStyle"); + const preset = localStorage.getItem('presetStyle'); const style = preset && (defaultStyles[preset] || localStorage.getItem(preset)); if (preset && style && JSON.isValid(style)) { @@ -706,8 +737,8 @@ function applyStyleOnLoad() { stylePreset.value = preset; stylePreset.dataset.old = preset; } else { - if (preset && preset !== "styleDefault" && ERROR) console.error(`Style preset ${preset} is not available in localStorage, applying default style`); - stylePreset.value = "styleDefault"; + if (preset && preset !== 'styleDefault' && ERROR) console.error(`Style preset ${preset} is not available in localStorage, applying default style`); + stylePreset.value = 'styleDefault'; stylePreset.dataset.old = preset; applyDefaultStyle(); } @@ -715,90 +746,161 @@ 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", .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", .9).attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("stroke-width", 1).attr("filter", "url(#dropShadow05)"); - stateBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt").attr("filter", null); - provinceBorders.attr("opacity", .8).attr("stroke", "#56566d").attr("stroke-width", .5).attr("stroke-dasharray", "0 2").attr("stroke-linecap", "round").attr("filter", null); - cells.attr("opacity", null).attr("stroke", "#808080").attr("stroke-width", .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", .8).attr("type", "pointyHex").attr("scale", 1).attr("dx", 0).attr("dy", 0).attr("stroke", "#777777").attr("stroke-width", .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", .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", .7).attr("stroke", "#777777").attr("stroke-width", 0).attr("filter", null); - cults.attr("opacity", .6).attr("stroke", "#777777").attr("stroke-width", .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", .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", .5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", .7).attr("filter", null); - lakes.select("#salt").attr("opacity", .5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", .7).attr("filter", null); - lakes.select("#sinkhole").attr("opacity", 1).attr("fill", "#5bc9fd").attr("stroke", "#53a3b0").attr("stroke-width", .7).attr("filter", null); - lakes.select("#frozen").attr("opacity", .95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null); - lakes.select("#lava").attr("opacity", .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", .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", .5).attr("stroke", "#1f3846").attr("stroke-width", .7).attr("auto-filter", 1).attr("filter", "url(#dropShadow)"); - coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", .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", .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", .9).attr("stroke", "#d06324").attr("stroke-width", .7).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt").attr("filter", null).attr("mask", null); - trails.attr("opacity", .9).attr("stroke", "#d06324").attr("stroke-width", .25).attr("stroke-dasharray", ".8 1.6").attr("stroke-linecap", "butt").attr("filter", null).attr("mask", null); - searoutes.attr("opacity", .8).attr("stroke", "#ffffff").attr("stroke-width", .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", .4).attr("filter", null); - statesHalo.attr("data-width", 10).attr("stroke-width", 10).attr("opacity", 1); - provs.attr("opacity", .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", .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", .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"); - oceanPattern.attr("opacity", .2); - svg.select("#oceanicPattern").attr("href", "./images/pattern1.png"); + 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'); + oceanPattern.attr('opacity', 0.2); + svg.select('#oceanicPattern').attr('href', './images/pattern1.png'); // 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", .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).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", .24).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("fill-opacity", .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) + .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).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", .5).attr("stroke-width", .12).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("fill-opacity", .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).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).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).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) + .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) + .attr('font-family', 'Almendra SC') + .attr('data-font', 'Almendra+SC') + .attr('font-size', 18) + .attr('data-size', 18) + .attr('filter', null); - fogging.attr("opacity", .98).attr("fill", "#30426f"); - emblems.attr("opacity", .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", .32).attr("filter", "url(#dropShadow01)"); + goods.attr('opacity', 1).attr('fill', '#000').attr('stroke', '#000').attr('stroke-width', 0.32).attr('filter', 'url(#dropShadow01)'); } // apply style settings in JSON @@ -808,7 +910,7 @@ function applyStyle(style) { if (!el) continue; for (const attribute in style[selector]) { const value = style[selector][attribute]; - if (value === "null" || value === null) el.removeAttribute(attribute); + if (value === 'null' || value === null) el.removeAttribute(attribute); else el.setAttribute(attribute, value); } } @@ -816,70 +918,76 @@ 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(); + + 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 + }; + confirmationDialog({title: 'Change style preset', message, confirm: 'Change', onConfirm}); } function updateElements() { // burgIcons to desired size - burgIcons.selectAll("g").each(function(d) { - const size = +this.getAttribute("size"); - d3.select(this).selectAll("circle").each(function() {this.setAttribute("r", size)}); - burgLabels.select("g#"+this.id).selectAll("text").each(function() {this.setAttribute("dy", `${size * -1.5}px`)}); + burgIcons.selectAll('g').each(function (d) { + const size = +this.getAttribute('size'); + d3.select(this) + .selectAll('circle') + .each(function () { + this.setAttribute('r', size); + }); + burgLabels + .select('g#' + this.id) + .selectAll('text') + .each(function () { + this.setAttribute('dy', `${size * -1.5}px`); + }); }); // anchor icons to desired size - anchors.selectAll("g").each(function(d) { - const size = +this.getAttribute("size"); - d3.select(this).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 * .47, 2)); - this.setAttribute("y", rn(y- size * .47, 2)); - this.setAttribute("width", size); - this.setAttribute("height", size); - }); + anchors.selectAll('g').each(function (d) { + const size = +this.getAttribute('size'); + d3.select(this) + .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); + }); }); // redraw elements - if (layerIsOn("toggleHeight")) drawHeightmap(); - if (legend.selectAll("*").size() && window.redrawLegend) redrawLegend(); - oceanLayers.selectAll("path").remove(); + if (layerIsOn('toggleHeight')) drawHeightmap(); + if (legend.selectAll('*').size() && window.redrawLegend) redrawLegend(); + oceanLayers.selectAll('path').remove(); OceanLayers(); invokeActiveZooming(); } 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); @@ -889,243 +997,301 @@ 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() {uploadFile(this, styleUpload)}); + 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"], - "#oceanPattern":["opacity"], - "#oceanicPattern":["href"], - "#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", "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", "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", "data-size", "font-size", "data-font", "font-family", "filter"], - "#labels > #addedLabels":["opacity", "fill", "stroke", "stroke-width", "data-size", "font-size", "data-font", "font-family", "filter"], - "#fogging":["opacity", "fill", "filter"] - }; + 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'], + '#oceanPattern': ['opacity'], + '#oceanicPattern': ['href'], + '#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', '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', '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', 'data-size', 'font-size', 'data-font', 'font-family', 'filter'], + '#labels > #addedLabels': ['opacity', 'fill', 'stroke', 'stroke-width', '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 => { + const s = (style[selector] = {}); + attributes[selector].forEach((attr) => { const el = document.querySelector(selector); if (!el) return; let value = el.getAttribute(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); }); } function parseValue(value) { - if (value === "null" || value === null) return null; - if (value === "") return ""; + if (value === 'null' || value === null) return null; + if (value === '') return ''; if (!isNaN(+value)) return +value; return value; } - + return style; } 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"; - else if (styleSaverName.value) tip = "new"; + if (!stored && listed) tip = 'default'; + else if (stored) tip = 'existing'; + else if (styleSaverName.value) tip = 'new'; styleSaverTip.innerHTML = tip; } function saveStyle() { - if (!styleSaverJSON.value) {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"); return}; - if (!styleSaverName.value) {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"); return}; - const preset = "style" + styleSaverName.value; + if (!styleSaverJSON.value) { + 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'); + return; + } + if (!styleSaverName.value) { + 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'); + return; + } + const preset = 'style' + styleSaverName.value; applyOption(stylePreset, preset, styleSaverName.value); // add option - localStorage.setItem("presetStyle", preset); // mark preset as default + localStorage.setItem('presetStyle', preset); // mark preset as default localStorage.setItem(preset, styleSaverJSON.value); // save preset - $("#styleSaver").dialog("close"); - removeStyleButton.style.display = "inline-block"; - tip("Style preset is saved", false, "warn", 4000); + $('#styleSaver').dialog('close'); + removeStyleButton.style.display = 'inline-block'; + tip('Style preset is saved', false, 'warn', 4000); } function styleDownload() { - if (!styleSaverJSON.value) {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"); return}; - if (!styleSaverName.value) {tip("Please provide a preset name", false, "error"); return}; + if (!styleSaverJSON.value) { + 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'); + return; + } + if (!styleSaverName.value) { + tip('Please provide a preset name', false, 'error'); + return; + } const data = styleSaverJSON.value; - if (!data) {tip("Please provide a style JSON", false, "error"); return}; - downloadFile(data, "style" + styleSaverName.value + ".json", "application/json"); + if (!data) { + tip('Please provide a style JSON', false, 'error'); + return; + } + 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"); return;} + if (!dataLoaded) { + tip('Cannot load the file. Please check the data format', false, 'error'); + return; + } const data = JSON.stringify(JSON.parse(dataLoaded), null, 2); styleSaverJSON.value = data; } } function removeStylePreset() { - if (stylePreset.selectedOptions[0].dataset.system) {tip("Cannot remove system preset", false, "error"); return;}; - localStorage.removeItem("presetStyle"); + if (stylePreset.selectedOptions[0].dataset.system) { + tip('Cannot remove system preset', false, 'error'); + return; + } + localStorage.removeItem('presetStyle'); localStorage.removeItem(stylePreset.value); stylePreset.selectedOptions[0].remove(); - removeStyleButton.style.display = "none"; + removeStyleButton.style.display = 'none'; } // GLOBAL FILTERS -mapFilters.addEventListener("click", applyMapFilter); +mapFilters.addEventListener('click', applyMapFilter); function applyMapFilter(event) { - if (event.target.tagName !== "BUTTON") return; + 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"); 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 + ")"); + 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 + ')'); } function updateMapFilter() { - const filter = svg.attr("data-filter"); - mapFilters.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); + 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"]; - fontsToAdd.forEach(function(f) {if (fonts.indexOf(f) === -1) fonts.push(f);}); + $('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); + }); updateFontOptions(); } } function fetchFonts(url) { return new Promise((resolve, reject) => { - if (url === "") { - tip("Use a direct link to any @font-face declaration or just font name to fetch from Google Fonts"); + if (url === '') { + tip('Use a direct link to any @font-face declaration or just font name to fetch from Google Fonts'); return; } - if (url.indexOf("http") === -1) { - url = url.replace(url.charAt(0), url.charAt(0).toUpperCase()).split(" ").join("+"); - url = "https://fonts.googleapis.com/css?family=" + url; + if (url.indexOf('http') === -1) { + url = url.replace(url.charAt(0), url.charAt(0).toUpperCase()).split(' ').join('+'); + url = 'https://fonts.googleapis.com/css?family=' + url; } - const fetched = addFonts(url).then(fetched => { + const fetched = addFonts(url).then((fetched) => { if (fetched === undefined) { - tip("Cannot fetch font for this value!", false, "error"); + tip('Cannot fetch font for this value!', false, 'error'); return; } if (fetched === 0) { - tip("Already in the fonts list!", false, "error"); + tip('Already in the fonts list!', false, 'error'); return; } updateFontOptions(); if (fetched === 1) { - tip("Font " + fonts[fonts.length - 1] + " is fetched"); + tip('Font ' + fonts[fonts.length - 1] + ' is fetched'); } else if (fetched > 1) { - tip(fetched + " fonts are added to the list"); + tip(fetched + ' fonts are added to the list'); } resolve(fetched); }); - }) + }); } function addFonts(url) { - $("head").append(''); + $('head').append(''); return fetch(url) - .then(resp => resp.text()) - .then(text => { + .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 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 font = family.replace(/['"]+/g, '').replace(/ /g, '+'); let weight = rule.style.getPropertyValue('font-weight'); - if (weight && weight !== "400") font += ":" + weight; + if (weight && weight !== '400') font += ':' + weight; if (fonts.indexOf(font) == -1) { fonts.push(font); - fetched++ + fetched++; } }; let fetched = 0; - for (let r of styleSheet.cssRules) {FontRule(r);} + for (let r of styleSheet.cssRules) { + FontRule(r); + } document.head.removeChild(s); return fetched; }) - .catch(function() {}); + .catch(function () {}); } // Update font list for Label and Burg Editors function updateFontOptions() { - styleSelectFont.innerHTML = ""; - for (let i=0; i < fonts.length; i++) { + styleSelectFont.innerHTML = ''; + for (let i = 0; i < fonts.length; i++) { 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); } -} \ No newline at end of file +} diff --git a/modules/ui/units-editor.js b/modules/ui/units-editor.js index 9c47a0ef..d6691bd2 100644 --- a/modules/ui/units-editor.js +++ b/modules/ui/units-editor.js @@ -1,57 +1,57 @@ -"use strict"; +'use strict'; function editUnits() { - closeDialogs("#unitsEditor, .stable"); - $("#unitsEditor").dialog(); + closeDialogs('#unitsEditor, .stable'); + $('#unitsEditor').dialog(); if (modules.editUnits) return; modules.editUnits = true; - $("#unitsEditor").dialog({ - title: "Units Editor", - position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"} + $('#unitsEditor').dialog({ + title: 'Units Editor', + position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'} }); // add listeners - document.getElementById("distanceUnitInput").addEventListener("change", changeDistanceUnit); - document.getElementById("distanceScaleOutput").addEventListener("input", changeDistanceScale); - document.getElementById("distanceScaleInput").addEventListener("change", changeDistanceScale); - document.getElementById("areaUnit").addEventListener("change", () => lock("areaUnit")); - document.getElementById("heightUnit").addEventListener("change", changeHeightUnit); - document.getElementById("heightExponentInput").addEventListener("input", changeHeightExponent); - document.getElementById("heightExponentOutput").addEventListener("input", changeHeightExponent); - document.getElementById("temperatureScale").addEventListener("change", changeTemperatureScale); - document.getElementById("barSizeOutput").addEventListener("input", changeScaleBarSize); - document.getElementById("barSize").addEventListener("input", changeScaleBarSize); - document.getElementById("barLabel").addEventListener("input", changeScaleBarLabel); - document.getElementById("barPosX").addEventListener("input", changeScaleBarPosition); - document.getElementById("barPosY").addEventListener("input", changeScaleBarPosition); - document.getElementById("barBackOpacity").addEventListener("input", changeScaleBarOpacity); - document.getElementById("barBackColor").addEventListener("input", changeScaleBarColor); + document.getElementById('distanceUnitInput').addEventListener('change', changeDistanceUnit); + document.getElementById('distanceScaleOutput').addEventListener('input', changeDistanceScale); + document.getElementById('distanceScaleInput').addEventListener('change', changeDistanceScale); + document.getElementById('areaUnit').addEventListener('change', () => lock('areaUnit')); + document.getElementById('heightUnit').addEventListener('change', changeHeightUnit); + document.getElementById('heightExponentInput').addEventListener('input', changeHeightExponent); + document.getElementById('heightExponentOutput').addEventListener('input', changeHeightExponent); + document.getElementById('temperatureScale').addEventListener('change', changeTemperatureScale); + document.getElementById('barSizeOutput').addEventListener('input', changeScaleBarSize); + document.getElementById('barSize').addEventListener('input', changeScaleBarSize); + document.getElementById('barLabel').addEventListener('input', changeScaleBarLabel); + document.getElementById('barPosX').addEventListener('input', changeScaleBarPosition); + document.getElementById('barPosY').addEventListener('input', changeScaleBarPosition); + document.getElementById('barBackOpacity').addEventListener('input', changeScaleBarOpacity); + document.getElementById('barBackColor').addEventListener('input', changeScaleBarColor); - document.getElementById("populationRateOutput").addEventListener("input", changePopulationRate); - document.getElementById("populationRate").addEventListener("change", changePopulationRate); - document.getElementById("urbanizationOutput").addEventListener("input", changeUrbanizationRate); - document.getElementById("urbanization").addEventListener("change", changeUrbanizationRate); + document.getElementById('populationRateOutput').addEventListener('input', changePopulationRate); + document.getElementById('populationRate').addEventListener('change', changePopulationRate); + document.getElementById('urbanizationOutput').addEventListener('input', changeUrbanizationRate); + document.getElementById('urbanization').addEventListener('change', changeUrbanizationRate); - document.getElementById("addLinearRuler").addEventListener("click", addRuler); - document.getElementById("addOpisometer").addEventListener("click", toggleOpisometerMode); - document.getElementById("addRouteOpisometer").addEventListener("click", toggleRouteOpisometerMode); - document.getElementById("addPlanimeter").addEventListener("click", togglePlanimeterMode); - document.getElementById("removeRulers").addEventListener("click", removeAllRulers); - document.getElementById("unitsRestore").addEventListener("click", restoreDefaultUnits); + document.getElementById('addLinearRuler').addEventListener('click', addRuler); + document.getElementById('addOpisometer').addEventListener('click', toggleOpisometerMode); + document.getElementById('addRouteOpisometer').addEventListener('click', toggleRouteOpisometerMode); + document.getElementById('addPlanimeter').addEventListener('click', togglePlanimeterMode); + document.getElementById('removeRulers').addEventListener('click', removeAllRulers); + document.getElementById('unitsRestore').addEventListener('click', restoreDefaultUnits); function changeDistanceUnit() { - if (this.value === "custom_name") { - prompt("Provide a custom name for a distance unit", {default:""}, custom => { + if (this.value === 'custom_name') { + prompt('Provide a custom name for a distance unit', {default: ''}, (custom) => { this.options.add(new Option(custom, custom, false, true)); - lock("distanceUnit"); + lock('distanceUnit'); drawScaleBar(); calculateFriendlyGridSize(); }); return; } - lock("distanceUnit"); + lock('distanceUnit'); drawScaleBar(); calculateFriendlyGridSize(); } @@ -59,285 +59,281 @@ function editUnits() { function changeDistanceScale() { const scale = +this.value; if (!scale || isNaN(scale) || scale < 0) { - tip("Distance scale should be a positive number", false, "error"); - this.value = document.getElementById("distanceScaleInput").dataset.value; + tip('Distance scale should be a positive number', false, 'error'); + this.value = document.getElementById('distanceScaleInput').dataset.value; return; } - document.getElementById("distanceScaleOutput").value = scale; - document.getElementById("distanceScaleInput").value = scale; - document.getElementById("distanceScaleInput").dataset.value = scale; - lock("distanceScale"); + document.getElementById('distanceScaleOutput').value = scale; + document.getElementById('distanceScaleInput').value = scale; + document.getElementById('distanceScaleInput').dataset.value = scale; + lock('distanceScale'); drawScaleBar(); calculateFriendlyGridSize(); } function changeHeightUnit() { - if (this.value === "custom_name") { - prompt("Provide a custom name for a height unit", {default:""}, custom => { + if (this.value === 'custom_name') { + prompt('Provide a custom name for a height unit', {default: ''}, (custom) => { this.options.add(new Option(custom, custom, false, true)); - lock("heightUnit"); + lock('heightUnit'); }); return; } - lock("heightUnit"); + lock('heightUnit'); } function changeHeightExponent() { - document.getElementById("heightExponentInput").value = this.value; - document.getElementById("heightExponentOutput").value = this.value; + document.getElementById('heightExponentInput').value = this.value; + document.getElementById('heightExponentOutput').value = this.value; calculateTemperatures(); - if (layerIsOn("toggleTemp")) drawTemp(); - lock("heightExponent"); + if (layerIsOn('toggleTemp')) drawTemp(); + lock('heightExponent'); } function changeTemperatureScale() { - lock("temperatureScale"); - if (layerIsOn("toggleTemp")) drawTemp(); + lock('temperatureScale'); + if (layerIsOn('toggleTemp')) drawTemp(); } function changeScaleBarSize() { - document.getElementById("barSize").value = this.value; - document.getElementById("barSizeOutput").value = this.value; + document.getElementById('barSize').value = this.value; + document.getElementById('barSizeOutput').value = this.value; drawScaleBar(); - lock("barSize"); + lock('barSize'); } function changeScaleBarPosition() { - lock("barPosX"); - lock("barPosY"); + lock('barPosX'); + lock('barPosY'); fitScaleBar(); } function changeScaleBarLabel() { - lock("barLabel"); + lock('barLabel'); drawScaleBar(); } function changeScaleBarOpacity() { - scaleBar.select("rect").attr("opacity", this.value); - lock("barBackOpacity"); + scaleBar.select('rect').attr('opacity', this.value); + lock('barBackOpacity'); } function changeScaleBarColor() { - scaleBar.select("rect").attr("fill", this.value); - lock("barBackColor"); + scaleBar.select('rect').attr('fill', this.value); + lock('barBackColor'); } function changePopulationRate() { const rate = +this.value; if (!rate || isNaN(rate) || rate <= 0) { - tip("Population rate should be a positive number", false, "error"); - this.value = document.getElementById("populationRate").dataset.value; + tip('Population rate should be a positive number', false, 'error'); + this.value = document.getElementById('populationRate').dataset.value; return; } - document.getElementById("populationRateOutput").value = rate; - document.getElementById("populationRate").value = rate; - document.getElementById("populationRate").dataset.value = rate; - lock("populationRate"); + document.getElementById('populationRateOutput').value = rate; + document.getElementById('populationRate').value = rate; + document.getElementById('populationRate').dataset.value = rate; + lock('populationRate'); } function changeUrbanizationRate() { const rate = +this.value; if (!rate || isNaN(rate) || rate < 0) { - tip("Urbanization rate should be a number", false, "error"); - this.value = document.getElementById("urbanization").dataset.value; + tip('Urbanization rate should be a number', false, 'error'); + this.value = document.getElementById('urbanization').dataset.value; return; } - document.getElementById("urbanizationOutput").value = rate; - document.getElementById("urbanization").value = rate; - document.getElementById("urbanization").dataset.value = rate; - lock("urbanization"); + document.getElementById('urbanizationOutput').value = rate; + document.getElementById('urbanization').value = rate; + document.getElementById('urbanization').dataset.value = rate; + lock('urbanization'); } function restoreDefaultUnits() { // distanceScale - document.getElementById("distanceScaleOutput").value = 3; - document.getElementById("distanceScaleInput").value = 3; - document.getElementById("distanceScaleInput").dataset.value = 3; - unlock("distanceScale"); + document.getElementById('distanceScaleOutput').value = 3; + document.getElementById('distanceScaleInput').value = 3; + document.getElementById('distanceScaleInput').dataset.value = 3; + unlock('distanceScale'); // units - const US = navigator.language === "en-US"; - const UK = navigator.language === "en-GB"; - distanceUnitInput.value = US || UK ? "mi" : "km"; - heightUnit.value = US || UK ? "ft" : "m"; - temperatureScale.value = US ? "°F" : "°C"; - areaUnit.value = "square"; - localStorage.removeItem("distanceUnit"); - localStorage.removeItem("heightUnit"); - localStorage.removeItem("temperatureScale"); - localStorage.removeItem("areaUnit"); + const US = navigator.language === 'en-US'; + const UK = navigator.language === 'en-GB'; + distanceUnitInput.value = US || UK ? 'mi' : 'km'; + heightUnit.value = US || UK ? 'ft' : 'm'; + temperatureScale.value = US ? '°F' : '°C'; + areaUnit.value = 'square'; + localStorage.removeItem('distanceUnit'); + localStorage.removeItem('heightUnit'); + localStorage.removeItem('temperatureScale'); + localStorage.removeItem('areaUnit'); calculateFriendlyGridSize(); // height exponent heightExponentInput.value = heightExponentOutput.value = 1.8; - localStorage.removeItem("heightExponent"); + localStorage.removeItem('heightExponent'); calculateTemperatures(); // scale bar barSizeOutput.value = barSize.value = 2; - barLabel.value = ""; - barBackOpacity.value = .2; - barBackColor.value = "#ffffff"; + barLabel.value = ''; + barBackOpacity.value = 0.2; + barBackColor.value = '#ffffff'; barPosX.value = barPosY.value = 99; - localStorage.removeItem("barSize"); - localStorage.removeItem("barLabel"); - localStorage.removeItem("barBackOpacity"); - localStorage.removeItem("barBackColor"); - localStorage.removeItem("barPosX"); - localStorage.removeItem("barPosY"); + localStorage.removeItem('barSize'); + localStorage.removeItem('barLabel'); + localStorage.removeItem('barBackOpacity'); + localStorage.removeItem('barBackColor'); + localStorage.removeItem('barPosX'); + localStorage.removeItem('barPosY'); drawScaleBar(); // population populationRateOutput.value = populationRate.value = 1000; urbanizationOutput.value = urbanization.value = 1; - localStorage.removeItem("populationRate"); - localStorage.removeItem("urbanization"); + localStorage.removeItem('populationRate'); + localStorage.removeItem('urbanization'); } function addRuler() { - if (!layerIsOn("toggleRulers")) toggleRulers(); + if (!layerIsOn('toggleRulers')) toggleRulers(); const pt = document.getElementById('map').createSVGPoint(); - pt.x = graphWidth / 2, pt.y = graphHeight / 4; + (pt.x = graphWidth / 2), (pt.y = graphHeight / 4); const p = pt.matrixTransform(viewbox.node().getScreenCTM().inverse()); const dx = graphWidth / 4 / scale; const dy = (rulers.data.length * 40) % (graphHeight / 2); - const from = [p.x-dx | 0, p.y+dy | 0]; - const to = [p.x+dx | 0, p.y+dy | 0]; + const from = [(p.x - dx) | 0, (p.y + dy) | 0]; + const to = [(p.x + dx) | 0, (p.y + dy) | 0]; rulers.create(Ruler, [from, to]).draw(); } function toggleOpisometerMode() { - if (this.classList.contains("pressed")) { + if (this.classList.contains('pressed')) { restoreDefaultEvents(); clearMainTip(); - this.classList.remove("pressed"); + this.classList.remove('pressed'); } else { - if (!layerIsOn("toggleRulers")) toggleRulers(); - tip("Draw a curve to measure length. Hold Shift to disallow path optimization", true); - unitsBottom.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); - this.classList.add("pressed"); - viewbox.style("cursor", "crosshair").call(d3.drag().on("start", function() { - const point = d3.mouse(this); - const opisometer = rulers.create(Opisometer, [point]).draw(); - - d3.event.on("drag", function() { + if (!layerIsOn('toggleRulers')) toggleRulers(); + tip('Draw a curve to measure length. Hold Shift to disallow path optimization', true); + unitsBottom.querySelectorAll('.pressed').forEach((button) => button.classList.remove('pressed')); + this.classList.add('pressed'); + viewbox.style('cursor', 'crosshair').call( + d3.drag().on('start', function () { const point = d3.mouse(this); - opisometer.addPoint(point); - }); + const opisometer = rulers.create(Opisometer, [point]).draw(); - d3.event.on("end", function() { - restoreDefaultEvents(); - clearMainTip(); - addOpisometer.classList.remove("pressed"); - if (opisometer.points.length < 2) rulers.remove(opisometer.id); - if (!d3.event.sourceEvent.shiftKey) opisometer.optimize(); - }); - })); + d3.event.on('drag', function () { + const point = d3.mouse(this); + opisometer.addPoint(point); + }); + + d3.event.on('end', function () { + restoreDefaultEvents(); + clearMainTip(); + addOpisometer.classList.remove('pressed'); + if (opisometer.points.length < 2) rulers.remove(opisometer.id); + if (!d3.event.sourceEvent.shiftKey) opisometer.optimize(); + }); + }) + ); } } function toggleRouteOpisometerMode() { - if (this.classList.contains("pressed")) { + if (this.classList.contains('pressed')) { restoreDefaultEvents(); clearMainTip(); - this.classList.remove("pressed"); + this.classList.remove('pressed'); } else { - if (!layerIsOn("toggleRulers")) toggleRulers(); - tip("Draw a curve along routes to measure length. Hold Shift to measure away from roads.", true); - unitsBottom.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); - this.classList.add("pressed"); - viewbox.style("cursor", "crosshair").call(d3.drag().on("start", function() { - const cells = pack.cells; - const burgs = pack.burgs; - const point = d3.mouse(this); - const c = findCell(point[0], point[1]); - if (cells.road[c] || d3.event.sourceEvent.shiftKey) { - const b = cells.burg[c]; - const x = b ? burgs[b].x : cells.p[c][0]; - const y = b ? burgs[b].y : cells.p[c][1]; - const routeOpisometer = rulers.create(RouteOpisometer, [[x, y]]).draw(); + if (!layerIsOn('toggleRulers')) toggleRulers(); + tip('Draw a curve along routes to measure length. Hold Shift to measure away from roads.', true); + unitsBottom.querySelectorAll('.pressed').forEach((button) => button.classList.remove('pressed')); + this.classList.add('pressed'); + viewbox.style('cursor', 'crosshair').call( + d3.drag().on('start', function () { + const cells = pack.cells; + const burgs = pack.burgs; + const point = d3.mouse(this); + const c = findCell(point[0], point[1]); + if (cells.road[c] || d3.event.sourceEvent.shiftKey) { + const b = cells.burg[c]; + const x = b ? burgs[b].x : cells.p[c][0]; + const y = b ? burgs[b].y : cells.p[c][1]; + const routeOpisometer = rulers.create(RouteOpisometer, [[x, y]]).draw(); - d3.event.on("drag", function () { - const point = d3.mouse(this); - const c = findCell(point[0], point[1]); - if (cells.road[c] || d3.event.sourceEvent.shiftKey) { - routeOpisometer.trackCell(c, true); - } - }); + d3.event.on('drag', function () { + const point = d3.mouse(this); + const c = findCell(point[0], point[1]); + if (cells.road[c] || d3.event.sourceEvent.shiftKey) { + routeOpisometer.trackCell(c, true); + } + }); - d3.event.on("end", function () { + d3.event.on('end', function () { + restoreDefaultEvents(); + clearMainTip(); + addRouteOpisometer.classList.remove('pressed'); + if (routeOpisometer.points.length < 2) { + rulers.remove(routeOpisometer.id); + } + }); + } else { restoreDefaultEvents(); clearMainTip(); - addRouteOpisometer.classList.remove("pressed"); - if (routeOpisometer.points.length < 2) { - rulers.remove(routeOpisometer.id); - } - }); - } else { - restoreDefaultEvents(); - clearMainTip(); - addRouteOpisometer.classList.remove("pressed"); - tip("Must start in a cell with a route in it", false, "error"); - } - })); + addRouteOpisometer.classList.remove('pressed'); + tip('Must start in a cell with a route in it', false, 'error'); + } + }) + ); } } function togglePlanimeterMode() { - if (this.classList.contains("pressed")) { + if (this.classList.contains('pressed')) { restoreDefaultEvents(); clearMainTip(); - this.classList.remove("pressed"); + this.classList.remove('pressed'); } else { - if (!layerIsOn("toggleRulers")) toggleRulers(); - tip("Draw a curve to measure its area. Hold Shift to disallow path optimization", true); - unitsBottom.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); - this.classList.add("pressed"); - viewbox.style("cursor", "crosshair").call(d3.drag().on("start", function() { - const point = d3.mouse(this); - const planimeter = rulers.create(Planimeter, [point]).draw(); - - d3.event.on("drag", function() { + if (!layerIsOn('toggleRulers')) toggleRulers(); + tip('Draw a curve to measure its area. Hold Shift to disallow path optimization', true); + unitsBottom.querySelectorAll('.pressed').forEach((button) => button.classList.remove('pressed')); + this.classList.add('pressed'); + viewbox.style('cursor', 'crosshair').call( + d3.drag().on('start', function () { const point = d3.mouse(this); - planimeter.addPoint(point); - }); + const planimeter = rulers.create(Planimeter, [point]).draw(); - d3.event.on("end", function() { - restoreDefaultEvents(); - clearMainTip(); - addPlanimeter.classList.remove("pressed"); - if (planimeter.points.length < 3) rulers.remove(planimeter.id); - else if (!d3.event.sourceEvent.shiftKey) planimeter.optimize(); - }); - })); + d3.event.on('drag', function () { + const point = d3.mouse(this); + planimeter.addPoint(point); + }); + d3.event.on('end', function () { + restoreDefaultEvents(); + clearMainTip(); + addPlanimeter.classList.remove('pressed'); + if (planimeter.points.length < 3) rulers.remove(planimeter.id); + else if (!d3.event.sourceEvent.shiftKey) planimeter.optimize(); + }); + }) + ); } } function removeAllRulers() { if (!rulers.data.length) return; - alertMessage.innerHTML = ` - Are you sure you want to remove all placed rulers? -
If you just want to hide rulers, toggle the Rulers layer off in Menu`; - $("#alert").dialog({resizable: false, title: "Remove all rulers", - buttons: { - Remove: function() { - $(this).dialog("close"); - rulers.undraw(); - rulers = new Rulers(); - }, - Cancel: function() {$(this).dialog("close");} - } - }); + + const message = 'Are you sure you want to remove all placed rulers?
If you just want to hide rulers, toggle the Rulers layer off in Menu'; + const onConfirm = () => { + rulers.undraw(); + rulers = new Rulers(); + }; + confirmationDialog({title: 'Remove all rulers', message, confirm: 'Remove', onConfirm}); } - - } -