annotate template strings for syntax hightlight and format

This commit is contained in:
Azgaar 2022-05-08 19:22:15 +03:00
parent 5a35f0d320
commit 1573fad58d
35 changed files with 816 additions and 460 deletions

View file

@ -1,7 +1,5 @@
// fill-box cannot use Shadow DOM as it needs access to svg hatches
// append stylesheet
{
const style = `
const style = /* css */ `
fill-box:not([disabled]) {
cursor: pointer;
}
@ -14,7 +12,8 @@
fill-box > svg > rect {
stroke: #666666;
stroke-width: 2;
}`;
}
`;
const styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
@ -24,7 +23,7 @@
{
const template = document.createElement("template");
template.innerHTML = `
template.innerHTML = /* html */ `
<svg>
<rect x="0" y="0" width="100%" height="100%">
</svg>
@ -70,5 +69,6 @@
}
}
// cannot use Shadow DOM here as need an access to svg hatches
customElements.define("fill-box", FillBox);
}

14
main.js
View file

@ -176,8 +176,8 @@ oceanLayers.append("rect").attr("id", "oceanBase").attr("x", 0).attr("y", 0).att
document.addEventListener("DOMContentLoaded", async () => {
if (!location.hostname) {
const wiki = "https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Run-FMG-locally";
alertMessage.innerHTML = `Fantasy Map Generator cannot run serverless.
Follow the <a href="${wiki}" target="_blank">instructions</a> on how you can easily run a local web-server`;
alertMessage.innerHTML = /* html */ `Fantasy Map Generator cannot run serverless. Follow the <a href="${wiki}" target="_blank">instructions</a> on how you can
easily run a local web-server`;
$("#alert").dialog({
resizable: false,
@ -455,9 +455,10 @@ function showWelcomeMessage() {
const discord = link("https://discordapp.com/invite/X7E84HU", "Discord server");
const patreon = link("https://www.patreon.com/azgaar", "Patreon");
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <strong>${version}</strong>.
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
<ul><strong>Latest changes:</strong>
alertMessage.innerHTML = /* html */ `The Fantasy Map Generator is updated up to version <strong>${version}</strong>. This version is compatible with ${changelog},
loaded <i>.map</i> files will be auto-updated.
<ul>
<strong>Latest changes:</strong>
<li>Submap tool by Goteguru</li>
<li>Resample tool by Goteguru</li>
<li>Pre-defined heightmaps</li>
@ -713,8 +714,7 @@ async function generate() {
const parsedError = parseError(error);
clearMainTip();
alertMessage.innerHTML = `An error has occurred on map generation. Please retry.
<br>If error is critical, clear the stored data and try again.
alertMessage.innerHTML = /* html */ `An error has occurred on map generation. Please retry. <br />If error is critical, clear the stored data and try again.
<p id="errorBox">${parsedError}</p>`;
$("#alert").dialog({
resizable: false,

View file

@ -41,7 +41,7 @@ window.BurgsAndStates = (function () {
if (sorted.length < count * 10) {
count = Math.floor(sorted.length / 10);
if (!count) {
WARN && console.warn(`There is no populated cells. Cannot generate states`);
WARN && console.warn("There is no populated cells. Cannot generate states");
return burgs;
} else {
WARN && console.warn(`Not enough populated cells (${sorted.length}). Will generate only ${count} states`);
@ -883,7 +883,7 @@ window.BurgsAndStates = (function () {
dd.forEach((r, d) => {
if (r !== "Ally" || states[d].diplomacy.includes("Vassal")) return;
if (states[d].diplomacy[attacker] !== "Rival" && ap / dp > 2 * gauss(1.6, 0.8, 0, 10, 2)) {
const reason = states[d].diplomacy.includes("Enemy") ? `Being already at war,` : `Frightened by ${an},`;
const reason = states[d].diplomacy.includes("Enemy") ? "Being already at war," : `Frightened by ${an},`;
war.push(`${reason} ${states[d].name} severed the defense pact with ${dn}`);
dd[d] = states[d].diplomacy[defender] = "Suspicion";
return;

View file

@ -15,9 +15,8 @@ window.Cultures = (function () {
if (!count) {
WARN && console.warn(`There are no populated cells. Cannot generate cultures`);
pack.cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round"}];
alertMessage.innerHTML = `
The climate is harsh and people cannot live in this world.<br>
No cultures, states and burgs will be created.<br>
alertMessage.innerHTML = /* html */ ` The climate is harsh and people cannot live in this world.<br />
No cultures, states and burgs will be created.<br />
Please consider changing climate settings in the World Configurator`;
$("#alert").dialog({
resizable: false,
@ -31,9 +30,8 @@ window.Cultures = (function () {
return;
} else {
WARN && console.warn(`Not enough populated cells (${populated.length}). Will generate only ${count} cultures`);
alertMessage.innerHTML = `
There are only ${populated.length} populated cells and it's insufficient livable area.<br>
Only ${count} out of ${culturesInput.value} requested cultures will be generated.<br>
alertMessage.innerHTML = /* html */ ` There are only ${populated.length} populated cells and it's insufficient livable area.<br />
Only ${count} out of ${culturesInput.value} requested cultures will be generated.<br />
Please consider changing climate settings in the World Configurator`;
$("#alert").dialog({
resizable: false,

View file

@ -69,7 +69,7 @@ export function resolveVersionConflicts(version) {
labels.selectAll("textPath").each(function () {
const text = this.textContent;
const shift = this.getComputedTextLength() / -1.5;
this.innerHTML = `<tspan x="${shift}">${text}</tspan>`;
this.innerHTML = /* html */ `<tspan x="${shift}">${text}</tspan>`;
});
// v1.0 added new biome - Wetland
@ -362,7 +362,7 @@ export function resolveVersionConflicts(version) {
const pattern = document.getElementById("oceanic");
const filter = pattern.firstElementChild.getAttribute("filter");
const href = filter ? "./images/" + filter.replace("url(#", "").replace(")", "") + ".png" : "";
pattern.innerHTML = `<image id="oceanicPattern" href=${href} width="100" height="100" opacity="0.2"></image>`;
pattern.innerHTML = /* html */ `<image id="oceanicPattern" href=${href} width="100" height="100" opacity="0.2"></image>`;
}
if (version < 1.62) {

View file

@ -50,7 +50,7 @@ function loadMapPrompt(blob) {
return;
}
alertMessage.innerHTML = `Are you sure you want to load saved map?<br>
alertMessage.innerHTML = /* html */ `Are you sure you want to load saved map?<br />
All unsaved changes made to the current map will be lost`;
$("#alert").dialog({
resizable: false,
@ -94,9 +94,10 @@ function loadMapFromURL(maplink, random) {
function showUploadErrorMessage(error, URL, random) {
ERROR && console.error(error);
alertMessage.innerHTML = `Cannot load map from the ${link(URL, "link provided")}.
${random ? `A new random map is generated. ` : ""}
Please ensure the linked file is reachable and CORS is allowed on server side`;
alertMessage.innerHTML = /* html */ `Cannot load map from the ${link(URL, "link provided")}. ${
random ? `A new random map is generated. ` : ""
} Please ensure the
linked file is reachable and CORS is allowed on server side`;
$("#alert").dialog({
title: "Loading error",
width: "32em",
@ -563,8 +564,7 @@ async function parseLoadedData(data) {
ERROR && console.error(error);
clearMainTip();
alertMessage.innerHTML = `An error is occured on map loading. Select a different file to load,
<br>generate a new random map or cancel the loading
alertMessage.innerHTML = /* html */ `An error is occured on map loading. Select a different file to load, <br />generate a new random map or cancel the loading
<p id="errorBox">${parseError(error)}</p>`;
$("#alert").dialog({

View file

@ -92,14 +92,28 @@ function editBiomes() {
totalArea += area;
totalPopulation += population;
lines += `<div class="states biomes" data-id="${i}" data-name="${b.name[i]}" data-habitability="${b.habitability[i]}"
data-cells=${b.cells[i]} data-area=${area} data-population=${population} data-color=${b.color[i]}>
lines += /* html */ `
<div
class="states biomes"
data-id="${i}"
data-name="${b.name[i]}"
data-habitability="${b.habitability[i]}"
data-cells=${b.cells[i]}
data-area=${area}
data-population=${population}
data-color=${b.color[i]}
>
<fill-box fill="${b.color[i]}"></fill-box>
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false">
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false" />
<span data-tip="Biome habitability percent" class="hide">%</span>
<input data-tip="Biome habitability percent. Click and set new value to change" type="number" min=0 max=9999 class="biomeHabitability hide" value=${
b.habitability[i]
}>
<input
data-tip="Biome habitability percent. Click and set new value to change"
type="number"
min="0"
max="9999"
class="biomeHabitability hide"
value=${b.habitability[i]}
/>
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="biomeCells hide">${b.cells[i]}</div>
<span data-tip="Biome area" style="padding-right: 4px" class="icon-map-o hide"></span>
@ -108,7 +122,8 @@ function editBiomes() {
<div data-tip="${populationTip}" class="biomePopulation hide">${si(population)}</div>
<span data-tip="Open Wikipedia article about the biome" class="icon-info-circled pointer hide"></span>
${i > 12 && !b.cells[i] ? '<span data-tip="Remove the custom biome" class="icon-trash-empty hide"></span>' : ""}
</div>`;
</div>
`;
}
body.innerHTML = lines;

View file

@ -281,10 +281,10 @@ function editBurg(id) {
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 burg group" : "the entire burg group"}?
<br>Please note that capital or locked burgs will not be deleted.
<br><br>Burgs to be removed: ${burgsToRemove.length}`;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${
basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
}?
<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${burgsToRemove.length}`;
$("#alert").dialog({
resizable: false,
title: "Remove burg group",
@ -545,7 +545,7 @@ function editBurg(id) {
function removeSelectedBurg() {
const id = +elSelected.attr("data-id");
if (pack.burgs[id].capital) {
alertMessage.innerHTML = `You cannot remove the burg as it is a state capital.<br><br>
alertMessage.innerHTML = /* html */ `You cannot remove the burg as it is a state capital.<br /><br />
You can change the capital using Burgs Editor (shift + T)`;
$("#alert").dialog({
resizable: false,

View file

@ -82,22 +82,30 @@ function overviewBurgs() {
const province = prov ? pack.provinces[prov].name : "";
const culture = pack.cultures[b.culture].name;
lines += `<div class="states" data-id=${b.i} data-name="${
b.name
}" data-state="${state}" data-province="${province}" data-culture="${culture}" data-population=${population} data-type="${type}">
lines += /* html */ `<div
class="states"
data-id=${b.i}
data-name="${b.name}"
data-state="${state}"
data-province="${province}"
data-culture="${culture}"
data-population=${population}
data-type="${type}"
>
<span data-tip="Click to zoom into view" class="icon-dot-circled pointer"></span>
<input data-tip="Burg name. Click and type to change" class="burgName" value="${b.name}" autocorrect="off" spellcheck="false">
<input data-tip="Burg province" class="burgState" value="${province}" disabled>
<input data-tip="Burg state" class="burgState" value="${state}" disabled>
<select data-tip="Dominant culture. Click to change burg culture (to change cell culture use Cultures Editor)" class="stateCulture">${getCultureOptions(
b.culture
)}</select>
<input data-tip="Burg name. Click and type to change" class="burgName" value="${b.name}" autocorrect="off" spellcheck="false" />
<input data-tip="Burg province" class="burgState" value="${province}" disabled />
<input data-tip="Burg state" class="burgState" value="${state}" disabled />
<select data-tip="Dominant culture. Click to change burg culture (to change cell culture use Cultures Editor)" class="stateCulture">
${getCultureOptions(b.culture)}
</select>
<span data-tip="Burg population" class="icon-male"></span>
<input data-tip="Burg population. Type to change" class="burgPopulation" value=${si(population)}>
<input data-tip="Burg population. Type to change" class="burgPopulation" value=${si(population)} />
<div class="burgType">
<span data-tip="${b.capital ? " This burg is a state capital" : "Click to assign a capital status"}" class="icon-star-empty${
b.capital ? "" : " inactive pointer"
}"></span>
<span
data-tip="${b.capital ? " This burg is a state capital" : "Click to assign a capital status"}"
class="icon-star-empty${b.capital ? "" : " inactive pointer"}"
></span>
<span data-tip="Click to toggle port status" class="icon-anchor pointer${b.port ? "" : " inactive"}" style="font-size:.9em"></span>
</div>
<span data-tip="Edit burg" class="icon-pencil"></span>
@ -312,11 +320,12 @@ function overviewBurgs() {
const treeLayout = d3.pack().size([w, h]).padding(3);
// prepare svg
alertMessage.innerHTML = `<select id="burgsTreeType" style="display:block; margin-left:13px; font-size:11px">
alertMessage.innerHTML = /* html */ `<select id="burgsTreeType" style="display:block; margin-left:13px; font-size:11px">
<option value="states" selected>Group by state</option>
<option value="cultures">Group by culture</option>
<option value="parent">Group by province and state</option>
<option value="provinces">Group by province</option></select>`;
<option value="provinces">Group by province</option>
</select>`;
alertMessage.innerHTML += `<div id='burgsInfo' class='chartInfo'>&#8205;</div>`;
const svg = d3
.select("#alertMessage")
@ -349,7 +358,7 @@ function overviewBurgs() {
const parent = d.parent.data.name;
const population = si(d.value * populationRate * urbanization);
burgsInfo.innerHTML = `${name}. ${parent}. Population: ${population}`;
burgsInfo.innerHTML = /* html */ `${name}. ${parent}. Population: ${population}`;
burgHighlightOn(ev);
tip("Click to zoom into view");
}
@ -483,9 +492,8 @@ function overviewBurgs() {
}
function renameBurgsInBulk() {
alertMessage.innerHTML = `Download burgs list as a text file, make changes and re-upload the file.
Make sure the file is a plain text document with each name on its own line (the dilimiter is CRLF).
If you do not want to change the name, just leave it as is`;
alertMessage.innerHTML = /* html */ `Download burgs list as a text file, make changes and re-upload the file. Make sure the file is a plain text document with each
name on its own line (the dilimiter is CRLF). If you do not want to change the name, just leave it as is`;
$("#alert").dialog({
title: "Burgs bulk renaming",

View file

@ -5,7 +5,8 @@ function editCoastline(node = d3.event.target) {
if (layerIsOn("toggleCells")) toggleCells();
$("#coastlineEditor").dialog({
title: "Edit Coastline", resizable: false,
title: "Edit Coastline",
resizable: false,
position: {my: "center top+20", at: "top", of: d3.event, collision: "fit"},
close: closeCoastlineEditor
});
@ -33,13 +34,27 @@ function editCoastline(node = d3.event.target) {
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))
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;
@ -48,12 +63,16 @@ function editCoastline(node = d3.event.target) {
}
function dragVertex() {
const x = rn(d3.event.x, 2), y = rn(d3.event.y, 2);
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();
}
@ -61,11 +80,14 @@ function editCoastline(node = d3.event.target) {
lineGen.curve(d3.curveBasisClosed);
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
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 area = Math.abs(d3.polygonArea(points));
@ -73,12 +95,12 @@ function editCoastline(node = d3.event.target) {
}
function showGroupSection() {
document.querySelectorAll("#coastlineEditor > button").forEach(el => el.style.display = "none");
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.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 = "";
@ -90,7 +112,7 @@ function editCoastline(node = d3.event.target) {
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));
});
}
@ -111,8 +133,14 @@ function editCoastline(node = d3.event.target) {
}
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");
@ -155,11 +183,14 @@ function editCoastline(node = d3.event.target) {
}
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 <i>sea_island</i> group`;
$("#alert").dialog({resizable: false, title: "Remove coastline group", width:"26em",
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the group? All coastline elements of the group (${count}) will be moved under
<i>sea_island</i> group`;
$("#alert").dialog({
resizable: false,
title: "Remove coastline group",
width: "26em",
buttons: {
Remove: function() {
Remove: function () {
$(this).dialog("close");
const sea = document.getElementById("sea_island");
const groupEl = document.getElementById(group);
@ -170,7 +201,9 @@ function editCoastline(node = d3.event.target) {
document.getElementById("coastlineGroup").selectedOptions[0].remove();
document.getElementById("coastlineGroup").value = "sea_island";
},
Cancel: function() {$(this).dialog("close");}
Cancel: function () {
$(this).dialog("close");
}
}
});
}

View file

@ -84,22 +84,37 @@ function editCultures() {
if (!c.i) {
// Uncultured (neutral) line
lines += `<div class="states" data-id=${c.i} data-name="${c.name}" data-color="" data-cells=${c.cells}
data-area=${area} data-population=${population} data-base=${c.base} data-type="" data-expansionism="" data-emblems="${c.shield}">
lines += /* html */ `<div
class="states"
data-id=${c.i}
data-name="${c.name}"
data-color=""
data-cells=${c.cells}
data-area=${area}
data-population=${population}
data-base=${c.base}
data-type=""
data-expansionism=""
data-emblems="${c.shield}"
>
<svg width="9" height="9" class="placeholder"></svg>
<input data-tip="Neutral culture name. Click and type to change" class="cultureName italic" value="${c.name}" autocorrect="off" spellcheck="false">
<input data-tip="Neutral culture name. Click and type to change" class="cultureName italic" value="${c.name}" autocorrect="off" spellcheck="false" />
<span class="icon-cw placeholder"></span>
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">${c.cells}</div>
<span class="icon-resize-full placeholder hide"></span>
<input class="statePower placeholder hide" type="number">
<select class="cultureType placeholder">${getTypeOptions(c.type)}</select>
<input class="statePower placeholder hide" type="number" />
<select class="cultureType placeholder">
${getTypeOptions(c.type)}
</select>
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Culture area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(c.base)}</select>
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">
${getBaseOptions(c.base)}
</select>
${
selectShape
? `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureShape hide">${getShapeOptions(c.shield)}</select>`
@ -109,24 +124,45 @@ function editCultures() {
continue;
}
lines += `<div class="states cultures" data-id=${c.i} data-name="${c.name}" data-color="${c.color}" data-cells=${c.cells}
data-area=${area} data-population=${population} data-base=${c.base} data-type=${c.type} data-expansionism=${c.expansionism} data-emblems="${c.shield}">
lines += /* html */ `<div
class="states cultures"
data-id=${c.i}
data-name="${c.name}"
data-color="${c.color}"
data-cells=${c.cells}
data-area=${area}
data-population=${population}
data-base=${c.base}
data-type=${c.type}
data-expansionism=${c.expansionism}
data-emblems="${c.shield}"
>
<fill-box fill="${c.color}"></fill-box>
<input data-tip="Culture name. Click and type to change" class="cultureName" value="${c.name}" autocorrect="off" spellcheck="false">
<input data-tip="Culture name. Click and type to change" class="cultureName" value="${c.name}" autocorrect="off" spellcheck="false" />
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">${c.cells}</div>
<span data-tip="Culture expansionism. Defines competitive size" class="icon-resize-full hide"></span>
<input data-tip="Culture expansionism. Defines competitive size. Click to change, then click Recalculate to apply change" class="statePower hide" type="number" min=0 max=99 step=.1 value=${
c.expansionism
}>
<select data-tip="Culture type. Defines growth model. Click to change" class="cultureType">${getTypeOptions(c.type)}</select>
<input
data-tip="Culture expansionism. Defines competitive size. Click to change, then click Recalculate to apply change"
class="statePower hide"
type="number"
min="0"
max="99"
step=".1"
value=${c.expansionism}
/>
<select data-tip="Culture type. Defines growth model. Click to change" class="cultureType">
${getTypeOptions(c.type)}
</select>
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Culture area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(c.base)}</select>
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">
${getBaseOptions(c.base)}
</select>
${
selectShape
? `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureShape hide">${getShapeOptions(c.shield)}</select>`
@ -200,7 +236,7 @@ function editCultures() {
const rural = c.rural * populationRate;
const urban = c.urban * populationRate * urbanization;
const population = rural + urban > 0 ? si(rn(rural + urban)) + " people" : "Extinct";
info.innerHTML = `${c.name} culture. ${c.type}. ${population}`;
info.innerHTML = /* html */ `${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");
}
@ -347,9 +383,8 @@ function editCultures() {
const l = n => Number(n).toLocaleString();
const burgs = pack.burgs.filter(b => !b.removed && b.culture === culture);
alertMessage.innerHTML = `
Rural: <input type="number" min=0 step=1 id="ruralPop" value=${rural} style="width:6em">
Urban: <input type="number" min=0 step=1 id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"}>
alertMessage.innerHTML = /* html */ ` Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
<p>Total population: ${l(total)} <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
const update = function () {
@ -898,8 +933,10 @@ function editCultures() {
const shapes = Object.keys(COA.shields.types)
.map(type => Object.keys(COA.shields[type]))
.flat();
const populated = pack.cells.pop.map((c, i) => c? i: null).filter(c => c);
cultures.forEach((item) => {if (item.i) item.removed = true});
const populated = pack.cells.pop.map((c, i) => (c ? i : null)).filter(c => c);
cultures.forEach(item => {
if (item.i) item.removed = true;
});
for (const c of csv.iterator((a, b) => +a[0] > +b[0])) {
let current;
if (+c.id < cultures.length) {
@ -934,7 +971,7 @@ function editCultures() {
current.base = nameBaseIndex === -1 ? 0 : nameBaseIndex;
}
cultures.filter(c => c.removed).forEach(c => removeCultureModel(c.i))
cultures.filter(c => c.removed).forEach(c => removeCultureModel(c.i));
drawCultures();
refreshCulturesEditor();

View file

@ -81,7 +81,7 @@ function editDiplomacy() {
const selectedName = states[selectedId].name;
COArenderer.trigger("stateCOA" + selectedId, states[selectedId].coa);
let lines = `<div class="states Self" data-id=${selectedId} data-tip="List below shows relations to ${selectedName}">
let lines = /* html */ `<div class="states Self" data-id=${selectedId} data-tip="List below shows relations to ${selectedName}">
<div style="width: max-content">${states[selectedId].fullName}</div>
<svg class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${selectedId}"></use></svg>
</div>`;
@ -98,7 +98,7 @@ function editDiplomacy() {
const name = state.fullName.length < 23 ? state.fullName : state.name;
COArenderer.trigger("stateCOA" + state.i, state.coa);
lines += `<div class="states" data-id=${state.i} data-name="${name}" data-relations="${relation}">
lines += /* html */ `<div class="states" data-id=${state.i} data-name="${name}" data-relations="${relation}">
<svg data-tip="${tipSelect}" class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${state.i}"></use></svg>
<div data-tip="${tipSelect}" style="width: 12em">${name}</div>
<div data-tip="${tipChange}" class="changeRelations" style="width: 6em">
@ -202,7 +202,7 @@ function editDiplomacy() {
const object = states[objectId];
const footer = `<div style="margin-top: 0.7em"><svg class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${object.i}"></use></svg> <b>${object.fullName}</b></div>`;
alertMessage.innerHTML = `<div style="overflow: hidden">${header} ${options} ${footer}</div>`;
alertMessage.innerHTML = /* html */ `<div style="overflow: hidden">${header} ${options} ${footer}</div>`;
$("#alert").dialog({
width: fitContent(),

View file

@ -60,7 +60,7 @@ function closeDialogs(except = "#except") {
function moveCircle(x, y, r = 20) {
let circle = document.getElementById("brushCircle");
if (!circle) {
const html = `<circle id="brushCircle" cx=${x} cy=${y} r=${r}></circle>`;
const html = /* html */ `<circle id="brushCircle" cx=${x} cy=${y} r=${r}></circle>`;
document.getElementById("debug").insertAdjacentHTML("afterBegin", html);
} else {
circle.setAttribute("cx", x);
@ -192,9 +192,9 @@ function moveBurgToGroup(id, g) {
function moveAllBurgsToGroup(fromGroup, toGroup) {
const groupToMove = document.querySelector(`#burgIcons #${fromGroup}`);
const burgsToMove = Array.from(groupToMove.children).map(x=>x.dataset.id);
addBurgsGroup(toGroup)
burgsToMove.forEach(x=>moveBurgToGroup(x, toGroup));
const burgsToMove = Array.from(groupToMove.children).map(x => x.dataset.id);
addBurgsGroup(toGroup);
burgsToMove.forEach(x => moveBurgToGroup(x, toGroup));
}
function addBurgsGroup(group) {
@ -495,18 +495,17 @@ function createPicker() {
.attr("width", 303)
.attr("height", 20)
.on("mousemove", () => tip("Color value in different color spaces. Edit to change"));
const html = `
<label style="margin-right: 6px">HSL:
<input type="number" id="pickerHSL_H" data-space="hsl" min=0 max=360 value="231">,
<input type="number" id="pickerHSL_S" data-space="hsl" min=0 max=100 value="70">,
<input type="number" id="pickerHSL_L" data-space="hsl" min=0 max=100 value="70">
const html = /* html */ ` <label style="margin-right: 6px"
>HSL: <input type="number" id="pickerHSL_H" data-space="hsl" min="0" max="360" value="231" />,
<input type="number" id="pickerHSL_S" data-space="hsl" min="0" max="100" value="70" />,
<input type="number" id="pickerHSL_L" data-space="hsl" min="0" max="100" value="70" />
</label>
<label style="margin-right: 6px">RGB:
<input type="number" id="pickerRGB_R" data-space="rgb" min=0 max=255 value="125">,
<input type="number" id="pickerRGB_G" data-space="rgb" min=0 max=255 value="142">,
<input type="number" id="pickerRGB_B" data-space="rgb" min=0 max=255 value="232">
<label style="margin-right: 6px"
>RGB: <input type="number" id="pickerRGB_R" data-space="rgb" min="0" max="255" value="125" />,
<input type="number" id="pickerRGB_G" data-space="rgb" min="0" max="255" value="142" />,
<input type="number" id="pickerRGB_B" data-space="rgb" min="0" max="255" value="232" />
</label>
<label>HEX: <input type="text" id="pickerHEX" data-space="hex" style="width:42px" autocorrect="off" spellcheck="false" value="#7d8ee8"></label>`;
<label>HEX: <input type="text" id="pickerHEX" data-space="hex" style="width:42px" autocorrect="off" spellcheck="false" value="#7d8ee8" /></label>`;
spaces.node().insertAdjacentHTML("beforeend", html);
spaces.selectAll("input").on("change", changePickerSpace);
@ -523,7 +522,7 @@ function createPicker() {
.attr("fill", d)
.attr("class", i ? "" : "selected")
.attr("x", (i % 14) * 22 + 4)
.attr("y", 40 + Math.floor(i / 14)*20)
.attr("y", 40 + Math.floor(i / 14) * 20)
.attr("width", 16)
.attr("height", 16);
});
@ -534,9 +533,9 @@ function createPicker() {
.attr("id", "picker_" + this.id)
.attr("fill", "url(#" + this.id + ")")
.attr("x", (i % 14) * 22 + 4)
.attr("y", Math.floor(i / 14)*20 + 20 + (number * 2))
.attr("y", Math.floor(i / 14) * 20 + 20 + number * 2)
.attr("width", 16)
.attr("height", 16)
.attr("height", 16);
});
colors
@ -546,7 +545,9 @@ function createPicker() {
hatches
.selectAll("rect")
.on("click", pickerFillClicked)
.on("mouseover", function() { tip("Click to fill with the hatching " + this.id) });
.on("mouseover", function () {
tip("Click to fill with the hatching " + this.id);
});
// append box
const bbox = picker.node().getBBox();
@ -562,10 +563,15 @@ function createPicker() {
.attr("fill", "#ffffff")
.attr("stroke", "#5d4651")
.on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", width-20).attr("y", -10).attr("id", "pickerCloseText").text("✕");
picker
.insert("text", ":first-child")
.attr("x", width - 20)
.attr("y", -10)
.attr("id", "pickerCloseText")
.text("✕");
picker
.insert("rect", ":first-child")
.attr("x", width-23)
.attr("x", width - 23)
.attr("y", -21)
.attr("id", "pickerCloseRect")
.attr("width", 14)

View file

@ -13,7 +13,10 @@ function editEmblem(type, id, el) {
updateElementSelectors(type, id, el);
$("#emblemEditor").dialog({
title: "Edit Emblem", resizable: true, width: "18.2em", height: "auto",
title: "Edit Emblem",
resizable: true,
width: "18.2em",
height: "auto",
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"},
close: closeEmblemEditor
});
@ -41,17 +44,17 @@ function editEmblem(type, id, el) {
function defineEmblemData(e) {
const parent = e.target.parentNode;
const [g, t] = parent.id === "burgEmblems" ? [pack.burgs, "burg"] :
parent.id === "provinceEmblems" ? [pack.provinces, "province"] :
[pack.states, "state"];
const [g, t] = parent.id === "burgEmblems" ? [pack.burgs, "burg"] : parent.id === "provinceEmblems" ? [pack.provinces, "province"] : [pack.states, "state"];
const i = +e.target.dataset.i;
type = t;
id = type+"COA"+i;
id = type + "COA" + i;
el = g[i];
}
function updateElementSelectors(type, id, el) {
let state = 0, province = 0, burg = 0;
let state = 0,
province = 0,
burg = 0;
// set active type
emblemStates.parentElement.className = type === "state" ? "active" : "";
@ -61,7 +64,7 @@ function editEmblem(type, id, el) {
// define selected values
if (type === "state") state = el.i;
else if (type === "province") {
province = el.i
province = el.i;
state = pack.states[el.state].i;
} else {
burg = el.i;
@ -85,7 +88,7 @@ function editEmblem(type, id, el) {
emblemBurgs.options.length = 0;
emblemBurgs.options.add(new Option("", 0, false, !burg));
const burgList = validBurgs.filter(burg => province ? pack.cells.province[burg.cell] === province : burg.state === state);
const burgList = validBurgs.filter(burg => (province ? pack.cells.province[burg.cell] === province : burg.state === state));
burgList.forEach(b => emblemBurgs.options.add(new Option(b.capital ? "👑 " + b.name : b.name, b.i, false, b.i === burg)));
emblemBurgs.options[0].disabled = true;
@ -116,14 +119,14 @@ function editEmblem(type, id, el) {
if (state) {
type = "state";
el = pack.states[state];
id = "stateCOA"+ state;
id = "stateCOA" + state;
} else {
// select neutral burg if state is changed to Neutrals
const neutralBurgs = pack.burgs.filter(burg => burg.i && !burg.removed && !burg.state);
if (!neutralBurgs.length) return;
type = "burg";
el = neutralBurgs[0];
id = "burgCOA"+ neutralBurgs[0].i;
id = "burgCOA" + neutralBurgs[0].i;
}
updateElementSelectors(type, id, el);
}
@ -134,13 +137,13 @@ function editEmblem(type, id, el) {
if (province) {
type = "province";
el = pack.provinces[province];
id = "provinceCOA"+ province;
id = "provinceCOA" + province;
} else {
// select state if province is changed to null value
const state = +emblemStates.value;
type = "state";
el = pack.states[state];
id = "stateCOA"+ state;
id = "stateCOA" + state;
}
updateElementSelectors(type, id, el);
@ -150,7 +153,7 @@ function editEmblem(type, id, el) {
const burg = +this.value;
type = "burg";
el = pack.burgs[burg];
id = "burgCOA"+ burg;
id = "burgCOA" + burg;
updateElementSelectors(type, id, el);
}
@ -166,23 +169,26 @@ function editEmblem(type, id, el) {
}
function changeSize() {
const size = el.coaSize = +this.value;
const size = (el.coaSize = +this.value);
document.getElementById("emblemSizeSlider").value = size;
document.getElementById("emblemSizeNumber").value = size;
const g = emblems.select("#"+type+"Emblems");
g.select("[data-i='"+el.i+"']").remove();
const g = emblems.select("#" + type + "Emblems");
g.select("[data-i='" + el.i + "']").remove();
if (!size) return;
// re-append use element
const categotySize = +g.attr("font-size");
const shift = categotySize * size / 2;
const shift = (categotySize * size) / 2;
const x = el.x || el.pole[0];
const y = el.y || el.pole[1];
g.append("use").attr("data-i", el.i)
.attr("x", rn(x - shift), 2).attr("y", rn(y - shift), 2)
.attr("width", size+"em").attr("height", size+"em")
.attr("href", "#"+id);
g.append("use")
.attr("data-i", el.i)
.attr("x", rn(x - shift), 2)
.attr("y", rn(y - shift), 2)
.attr("width", size + "em")
.attr("height", size + "em")
.attr("href", "#" + id);
}
function regenerate() {
@ -194,7 +200,7 @@ function editEmblem(type, id, el) {
}
const shield = el.coa.shield || COA.getShield(el.culture || parent?.culture || 0, el.state);
el.coa = COA.generate(parent ? parent.coa : null, .3, .1, null);
el.coa = COA.generate(parent ? parent.coa : null, 0.3, 0.1, null);
el.coa.shield = shield;
emblemShapeSelector.disabled = false;
emblemShapeSelector.value = el.coa.shield;
@ -229,7 +235,7 @@ function editEmblem(type, id, el) {
const reader = new FileReader();
reader.onload = function(readerEvent) {
reader.onload = function (readerEvent) {
const result = readerEvent.target.result;
const defs = document.getElementById("defs-emblems");
const coa = document.getElementById(id); // old emblem
@ -266,7 +272,8 @@ function editEmblem(type, id, el) {
emblemShapeSelector.disabled = true;
};
if (type === "image") reader.readAsDataURL(file); else reader.readAsText(file);
if (type === "image") reader.readAsDataURL(file);
else reader.readAsText(file);
}
function toggleDownload() {
@ -282,7 +289,8 @@ function editEmblem(type, id, el) {
const link = document.createElement("a");
link.download = getFileName(`Emblem ${el.fullName || el.name}`) + "." + format;
if (format === "svg") downloadSVG(url, link); else downloadRaster(format, url, link, size);
if (format === "svg") downloadSVG(url, link);
else downloadRaster(format, url, link, size);
document.getElementById("emblemDownloadControl").classList.add("hidden");
}
@ -299,22 +307,22 @@ function editEmblem(type, id, el) {
const img = new Image();
img.src = url;
img.onload = function() {
img.onload = function () {
if (format === "jpeg") {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
const dataURL = canvas.toDataURL("image/" + format, .92);
const dataURL = canvas.toDataURL("image/" + format, 0.92);
link.href = dataURL;
link.click();
window.setTimeout(() => window.URL.revokeObjectURL(dataURL), 6000);
}
};
}
async function getURL(svg, size) {
const serialized = getSVG(svg, size);
const blob = new Blob([serialized], {type: 'image/svg+xml;charset=utf-8'});
const blob = new Blob([serialized], {type: "image/svg+xml;charset=utf-8"});
const url = window.URL.createObjectURL(blob);
window.setTimeout(() => window.URL.revokeObjectURL(url), 6000);
return url;
@ -324,7 +332,7 @@ function editEmblem(type, id, el) {
const clone = svg.cloneNode(true);
clone.setAttribute("width", size);
clone.setAttribute("height", size);
return (new XMLSerializer()).serializeToString(clone);
return new XMLSerializer().serializeToString(clone);
}
async function downloadGallery() {
@ -338,70 +346,135 @@ function editEmblem(type, id, el) {
function runDownload() {
const back = `<a href="javascript:history.back()">Go Back</a>`;
const stateSection = `<div><h2>States</h2>` + validStates.map(state => {
const el = document.getElementById("stateCOA"+state.i);
const stateSection =
`<div><h2>States</h2>` +
validStates
.map(state => {
const el = document.getElementById("stateCOA" + state.i);
return `<figure id="state_${state.i}"><a href="#provinces_${state.i}"><figcaption>${state.fullName}</figcaption>${getSVG(el, 200)}</a></figure>`;
}).join("") + `</div>`;
})
.join("") +
`</div>`;
const provinceSections = validStates.map(state => {
const provinceSections = validStates
.map(state => {
const stateProvinces = validProvinces.filter(p => p.state === state.i);
const figures = stateProvinces.map(province => {
const el = document.getElementById("provinceCOA"+province.i);
return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${province.fullName}</figcaption>${getSVG(el, 200)}</a></figure>`;
}).join("");
const figures = stateProvinces
.map(province => {
const el = document.getElementById("provinceCOA" + province.i);
return `<figure id="province_${province.i}"><a href="#burgs_${province.i}"><figcaption>${province.fullName}</figcaption>${getSVG(
el,
200
)}</a></figure>`;
})
.join("");
return stateProvinces.length ? `<div id="provinces_${state.i}">${back}<h2>${state.fullName} provinces</h2>${figures}</div>` : "";
}).join("");
})
.join("");
const burgSections = validStates.map(state => {
const burgSections = validStates
.map(state => {
const stateBurgs = validBurgs.filter(b => b.state === state.i);
let stateBurgSections = validProvinces.filter(p => p.state === state.i).map(province => {
let stateBurgSections = validProvinces
.filter(p => p.state === state.i)
.map(province => {
const provinceBurgs = stateBurgs.filter(b => pack.cells.province[b.cell] === province.i);
const provinceBurgFigures = provinceBurgs.map(burg => {
const el = document.getElementById("burgCOA"+burg.i);
const provinceBurgFigures = provinceBurgs
.map(burg => {
const el = document.getElementById("burgCOA" + burg.i);
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
}).join("");
})
.join("");
return provinceBurgs.length ? `<div id="burgs_${province.i}">${back}<h2>${province.fullName} burgs</h2>${provinceBurgFigures}</div>` : "";
}).join("");
})
.join("");
const stateBurgOutOfProvinces = stateBurgs.filter(b => !pack.cells.province[b.cell]);
const stateBurgOutOfProvincesFigures = stateBurgOutOfProvinces.map(burg => {
const el = document.getElementById("burgCOA"+burg.i);
const stateBurgOutOfProvincesFigures = stateBurgOutOfProvinces
.map(burg => {
const el = document.getElementById("burgCOA" + burg.i);
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
}).join("");
if (stateBurgOutOfProvincesFigures) stateBurgSections += `<div><h2>${state.fullName} burgs under direct control</h2>${stateBurgOutOfProvincesFigures}</div>`;
})
.join("");
if (stateBurgOutOfProvincesFigures)
stateBurgSections += `<div><h2>${state.fullName} burgs under direct control</h2>${stateBurgOutOfProvincesFigures}</div>`;
return stateBurgSections;
}).join("");
})
.join("");
const neutralBurgs = validBurgs.filter(b => !b.state);
const neutralsSection = neutralBurgs.length ? "<div><h2>Independent burgs</h2>" + neutralBurgs.map(burg => {
const el = document.getElementById("burgCOA"+burg.i);
const neutralsSection = neutralBurgs.length
? "<div><h2>Independent burgs</h2>" +
neutralBurgs
.map(burg => {
const el = document.getElementById("burgCOA" + burg.i);
return `<figure id="burg_${burg.i}"><figcaption>${burg.name}</figcaption>${getSVG(el, 200)}</figure>`;
}).join("") + "</div>" : "";
})
.join("") +
"</div>"
: "";
const FMG = `<a href="https://azgaar.github.io/Fantasy-Map-Generator" target="_blank">Azgaar's Fantasy Map Generator</a>`;
const license = `<a target="_blank" href="https://github.com/Azgaar/Armoria#license">the license</a>`;
const html = `<!DOCTYPE html><html><head><title>${mapName.value} Emblems Gallery</title></head>
const html = /* html */ `<!DOCTYPE html>
<html>
<head>
<title>${mapName.value} Emblems Gallery</title>
</head>
<style type="text/css">
body { margin: 0; padding: 1em; font-family: serif; }
h1, h2 { font-family: 'Forum'; }
div { width: 100%; max-width: 1018px; margin: 0 auto; border-bottom: 1px solid #ddd; }
figure { margin: 0 0 2em; display: inline-block; transition: .2s; }
figure:hover { background-color: #f6f6f6; }
figcaption { text-align: center; margin: .4em 0; width: 200px; font-family: 'Overlock SC' }
address { width: 100%; max-width: 1018px; margin: 0 auto; }
a { color: black; }
figure > a { text-decoration: none; }
div > a { float: right; font-family: monospace; margin-top: .8em; }
body {
margin: 0;
padding: 1em;
font-family: serif;
}
h1,
h2 {
font-family: "Forum";
}
div {
width: 100%;
max-width: 1018px;
margin: 0 auto;
border-bottom: 1px solid #ddd;
}
figure {
margin: 0 0 2em;
display: inline-block;
transition: 0.2s;
}
figure:hover {
background-color: #f6f6f6;
}
figcaption {
text-align: center;
margin: 0.4em 0;
width: 200px;
font-family: "Overlock SC";
}
address {
width: 100%;
max-width: 1018px;
margin: 0 auto;
}
a {
color: black;
}
figure > a {
text-decoration: none;
}
div > a {
float: right;
font-family: monospace;
margin-top: 0.8em;
}
</style>
<link href="https://fonts.googleapis.com/css2?family=Forum&family=Overlock+SC" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Forum&family=Overlock+SC" rel="stylesheet" />
<body>
<div><h1>${mapName.value} Emblems Gallery</h1></div>
${stateSection}
${provinceSections}
${burgSections}
${neutralsSection}
${stateSection} ${provinceSections} ${burgSections} ${neutralsSection}
<address>Generated by ${FMG}. The tool is free, but images may be copyrighted, see ${license}</address>
</body></html>`;
</body>
</html>`;
downloadFile(html, name + ".html", "text/plain");
}
}
@ -409,9 +482,9 @@ function editEmblem(type, id, el) {
async function renderAllEmblems(states, provinces, burgs) {
tip("Preparing for download...", true, "warn");
const statePromises = states.map(state => COArenderer.trigger("stateCOA"+state.i, state.coa));
const provincePromises = provinces.map(province => COArenderer.trigger("provinceCOA"+province.i, province.coa));
const burgPromises = burgs.map(burg => COArenderer.trigger("burgCOA"+burg.i, burg.coa));
const statePromises = states.map(state => COArenderer.trigger("stateCOA" + state.i, state.coa));
const provincePromises = provinces.map(province => COArenderer.trigger("provinceCOA" + province.i, province.coa));
const burgPromises = burgs.map(burg => COArenderer.trigger("burgCOA" + burg.i, burg.coa));
const promises = [...statePromises, ...provincePromises, ...burgPromises];
return Promise.allSettled(promises).then(res => clearMainTip());
@ -419,10 +492,11 @@ function editEmblem(type, id, el) {
function dragEmblem() {
const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y;
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)})`;
d3.event.on("drag", function () {
const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
this.setAttribute("transform", transform);
});
}

View file

@ -405,7 +405,7 @@ document.querySelectorAll("[data-locked]").forEach(function (e) {
// lock option
function lock(id) {
const input = document.querySelector("[data-stored=\"" + id + "\"]");
const input = document.querySelector('[data-stored="' + id + '"]');
if (input) localStorage.setItem(id, input.value);
const el = document.getElementById("lock_" + id);
if (!el) return;
@ -467,15 +467,18 @@ function showInfo() {
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
const VideoTutorial = link("https://youtube.com/playlist?list=PLtgiuDC8iVR2gIG8zMTRn7T_L0arl9h1C", "Video tutorial");
alertMessage.innerHTML = `
<b>Fantasy Map Generator</b> (FMG) is a free open-source application.
It means that you own all created maps and can use them as you wish.
alertMessage.innerHTML = /* html */ `<b>Fantasy Map Generator</b> (FMG) is a free open-source application. It means that you own all created maps and can use them as
you wish.
<p>The development is community-backed, you can donate on ${Patreon}.
You can also help creating overviews, tutorials and spreding the word about the Generator.</p>
<p>
The development is community-backed, you can donate on ${Patreon}. You can also help creating overviews, tutorials and spreding the word about the
Generator.
</p>
<p>The best way to get help is to contact the community on ${Discord} and ${Reddit}.
Before asking questions, please check out the ${QuickStart}, the ${QAA}, and ${VideoTutorial}.</p>
<p>
The best way to get help is to contact the community on ${Discord} and ${Reddit}. Before asking questions, please check out the ${QuickStart}, the ${QAA},
and ${VideoTutorial}.
</p>
<p>Check out our another project: ${Armoria} heraldry generator and editor.</p>

View file

@ -2,12 +2,14 @@
function editHeightmap() {
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 <i>erase</i> the secondary data and let the system automatically regenerate it on edit completion.
alertMessage.innerHTML = /* html */ `Heightmap is a core element on which all other data (rivers, burgs, states etc) is based. So the best edit approach is to
<i>erase</i> the secondary data and let the system automatically regenerate it on edit completion.
<p><i>Erase</i> mode also allows you Convert an Image into a heightmap or use Template Editor.</p>
<p>You can <i>keep</i> the data, but you won't be able to change the coastline.</p>
<p>Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.</p>
<p>Please <span class="pseudoLink" onclick=dowloadMap(); editHeightmap();>save the map</span> before editing the heightmap!</p>
<p>
Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.
</p>
<p>Please <span class="pseudoLink" onclick="dowloadMap();" editHeightmap();>save the map</span> before editing the heightmap!</p>
<p style="margin-bottom: 0">Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>`;
$("#alert").dialog({
@ -107,7 +109,7 @@ function editHeightmap() {
heightmapInfoX.innerHTML = rn(p[0]);
heightmapInfoY.innerHTML = rn(p[1]);
heightmapInfoCell.innerHTML = cell;
heightmapInfoHeight.innerHTML = `${grid.cells.h[cell]} (${getHeight(grid.cells.h[cell])})`;
heightmapInfoHeight.innerHTML = /* html */ `${grid.cells.h[cell]} (${getHeight(grid.cells.h[cell])})`;
if (tooltip.dataset.main) showMainTip();
// move radius circle if drag mode is active
@ -485,7 +487,7 @@ function editHeightmap() {
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)}%)`;
landmassCounter.innerHTML = /* html */ `${landCells} (${rn((landCells / grid.cells.i.length) * 100)}%)`;
landmassAverage.innerHTML = rn(d3.mean(grid.cells.h));
}
@ -1247,11 +1249,8 @@ function editHeightmap() {
function closeImageConverter(event) {
event.preventDefault();
event.stopPropagation();
alertMessage.innerHTML = `
Are you sure you want to close the Image Converter?
Click "Cancel" to geck back to convertion.
Click "Complete" to apply the conversion.
Click "Close" to exit conversion mode and restore previous heightmap`;
alertMessage.innerHTML = /* html */ ` Are you sure you want to close the Image Converter? Click "Cancel" to geck back to convertion. Click "Complete" to apply
the conversion. Click "Close" to exit conversion mode and restore previous heightmap`;
$("#alert").dialog({
resizable: false,

View file

@ -12,7 +12,8 @@ function editIce() {
ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement));
$("#iceEditor").dialog({
title: "Edit "+type, resizable: false,
title: "Edit " + type,
resizable: false,
position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"},
close: closeEditor
});
@ -30,8 +31,9 @@ function editIce() {
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 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);
}
@ -39,10 +41,13 @@ function editIce() {
function changeSize() {
const c = grid.points[+elSelected.attr("cell")];
const s = +elSelected.attr("size");
const flat = elSelected.attr("points").split(",").map(el => +el);
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);
@ -65,7 +70,7 @@ function editIce() {
const c = grid.points[i];
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 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();
@ -73,26 +78,32 @@ function editIce() {
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,
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`;
$("#alert").dialog({
resizable: false,
title: "Remove " + type,
buttons: {
Remove: function() {
Remove: function () {
$(this).dialog("close");
elSelected.remove();
$("#iceEditor").dialog("close");
},
Cancel: function() {$(this).dialog("close");}
Cancel: function () {
$(this).dialog("close");
}
}
});
}
function dragElement() {
const tr = parseTransform(this.getAttribute("transform"));
const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y;
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})`);
});
}

View file

@ -257,9 +257,10 @@ function editLabel() {
const group = elSelected.node().parentNode.id;
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"}?
<br><br>Labels to be removed: ${count}`;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${
basic ? "all elements in the group" : "the entire label group"
}? <br /><br />Labels to be
removed: ${count}`;
$("#alert").dialog({
resizable: false,
title: "Remove route group",

View file

@ -5,7 +5,8 @@ function editLake() {
if (layerIsOn("toggleCells")) toggleCells();
$("#lakeEditor").dialog({
title: "Edit Lake", resizable: false,
title: "Edit Lake",
resizable: false,
position: {my: "center top+20", at: "top", of: d3.event, collision: "fit"},
close: closeLakesEditor
});
@ -71,23 +72,41 @@ function editLake() {
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))
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);
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();
}
@ -97,7 +116,7 @@ function editLake() {
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
defs.select("mask#land > path#land_" + feature.i).attr("d", d); // update land mask
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
feature.area = Math.abs(d3.polygonArea(points));
@ -115,7 +134,7 @@ 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) {
@ -123,7 +142,7 @@ function editLake() {
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));
});
}
@ -145,8 +164,14 @@ function editLake() {
}
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");
@ -189,11 +214,13 @@ function editLake() {
}
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",
alertMessage.innerHTML = /* html */ `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() {
Remove: function () {
$(this).dialog("close");
const freshwater = document.getElementById("freshwater");
const groupEl = document.getElementById(group);
@ -204,7 +231,9 @@ function editLake() {
document.getElementById("lakeGroup").selectedOptions[0].remove();
document.getElementById("lakeGroup").value = "freshwater";
},
Cancel: function() {$(this).dialog("close");}
Cancel: function () {
$(this).dialog("close");
}
}
});
}

View file

@ -75,18 +75,30 @@ function overviewMilitary() {
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
lines += `<div class="states" data-id=${s.i} data-state="${s.name}" ${sortData} data-total="${total}"
data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
lines += /* html */ `<div
class="states"
data-id=${s.i}
data-state="${s.name}"
${sortData}
data-total="${total}"
data-population="${population}"
data-rate="${rate}"
data-alert="${s.alert}"
>
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly />
${lineData}
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>
<div data-type="population" data-tip="State population">${si(population)}</div>
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(rate, 2)}%</div>
<input data-tip="War Alert. Editable modifier to military forces number, depends of political situation" style="width:4.1em" type="number" min=0 step=.01 value="${rn(
s.alert,
2
)}">
<input
data-tip="War Alert. Editable modifier to military forces number, depends of political situation"
style="width:4.1em"
type="number"
min="0"
step=".01"
value="${rn(s.alert, 2)}"
/>
<span data-tip="Show regiments list" class="icon-list-bullet pointer"></span>
</div>`;
}
@ -290,21 +302,28 @@ function overviewMilitary() {
${getLimitText(unit[attr])}
</button>`;
row.innerHTML = `<td><button data-type="icon" data-tip="Click to select unit icon">${icon || " "}</button></td>
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${name}"></td>
row.innerHTML = /* html */ `<td><button data-type="icon" data-tip="Click to select unit icon">${icon || " "}</button></td>
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${name}" /></td>
<td>${getLimitButton("biomes")}</td>
<td>${getLimitButton("states")}</td>
<td>${getLimitButton("cultures")}</td>
<td>${getLimitButton("religions")}</td>
<td><input data-tip="Enter conscription percentage for rural population" type="number" min=0 max=100 step=.01 value="${rural}"></td>
<td><input data-tip="Enter conscription percentage for urban population" type="number" min=0 max=100 step=.01 value="${urban}"></td>
<td><input data-tip="Enter average number of people in crew (for total personnel calculation)" type="number" min=1 step=1 value="${crew}"></td>
<td><input data-tip="Enter military power (used for battle simulation)" type="number" min=0 step=.1 value="${power}"></td>
<td><select data-tip="Select unit type to apply special rules on forces recalculation">${typeOptions}</select></td>
<td><input data-tip="Enter conscription percentage for rural population" type="number" min="0" max="100" step=".01" value="${rural}" /></td>
<td><input data-tip="Enter conscription percentage for urban population" type="number" min="0" max="100" step=".01" value="${urban}" /></td>
<td><input data-tip="Enter average number of people in crew (for total personnel calculation)" type="number" min="1" step="1" value="${crew}" /></td>
<td><input data-tip="Enter military power (used for battle simulation)" type="number" min="0" step=".1" value="${power}" /></td>
<td>
<select data-tip="Select unit type to apply special rules on forces recalculation">
${typeOptions}
</select>
</td>
<td data-tip="Check if unit is <b>separate</b> and can be stacked only with the same units">
<input id="${name}Separate" type="checkbox" class="checkbox" ${separate ? "checked" : ""}>
<label for="${name}Separate" class="checkbox-label"></label></td>
<td data-tip="Remove the unit"><span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span></td>`;
<input id="${name}Separate" type="checkbox" class="checkbox" ${separate ? "checked" : ""} />
<label for="${name}Separate" class="checkbox-label"></label>
</td>
<td data-tip="Remove the unit">
<span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span>
</td>`;
tableBody.appendChild(row);
}
@ -326,7 +345,12 @@ function overviewMilitary() {
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
</td></tr>`
);
alertMessage.innerHTML = `<b>Limit unit by ${type}:</b><table style="margin-top:.3em"><tbody>${lines.join("")}</tbody></table>`;
alertMessage.innerHTML = /* html */ `<b>Limit unit by ${type}:</b>
<table style="margin-top:.3em">
<tbody>
${lines.join("")}
</tbody>
</table>`;
$("#alert").dialog({
width: fitContent(),

View file

@ -154,15 +154,15 @@ function editNamesbase() {
return "<span data-tip='Namesbase variety is good' style='color:green'>[good]</span>";
};
alertMessage.innerHTML = `<div style="line-height: 1.6em; max-width: 20em">
alertMessage.innerHTML = /* html */ `<div style="line-height: 1.6em; max-width: 20em">
<div data-tip="Number of names provided">Namesbase length: ${length} ${getLengthQuality()}</div>
<div data-tip="Average number of generation variants for each key in the chain">Namesbase variety: ${variety} ${getVarietyLevel()}</div>
<hr>
<hr />
<div data-tip="The shortest name length">Min name length: ${d3.min(wordsLength)}</div>
<div data-tip="The longest name length">Max name length: ${d3.max(wordsLength)}</div>
<div data-tip="Average name length">Mean name length: ${rn(d3.mean(wordsLength), 1)}</div>
<div data-tip="Common name length">Median name length: ${d3.median(wordsLength)}</div>
<hr>
<hr />
<div data-tip="Characters outside of Basic Latin have bad font support">Non-basic chars: ${nonBasicLatinChars}</div>
<div data-tip="Characters that are frequently (more than 3 times) doubled">Doubled chars: ${doubled.join("")}</div>
<div data-tip="Names used more than one time">Duplicates: ${duplicates}</div>
@ -196,7 +196,7 @@ function editNamesbase() {
}
function namesbaseRestoreDefault() {
alertMessage.innerHTML = `Are you sure you want to restore default namesbase?`;
alertMessage.innerHTML = /* html */ `Are you sure you want to restore default namesbase?`;
$("#alert").dialog({
resizable: false,
title: "Restore default data",

View file

@ -296,7 +296,9 @@ function showSeedHistoryDialog() {
const button = `<i data-tip="Click to generate a map with this seed" onclick="restoreSeed(${i})" class="icon-history optionsSeedRestore"></i>`;
return `<li>Seed: ${h.seed} ${button}. Size: ${h.width}x${h.height}. Template: ${h.template}. Created: ${created}</li>`;
});
alertMessage.innerHTML = `<ol style="margin: 0; padding-left: 1.5em">${lines.join("")}</ol>`;
alertMessage.innerHTML = /* html */ `<ol style="margin: 0; padding-left: 1.5em">
${lines.join("")}
</ol>`;
$("#alert").dialog({
resizable: false,
@ -677,7 +679,7 @@ function regeneratePrompt(source) {
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
if (workingTime < 5) return regenerateMap(source);
alertMessage.innerHTML = `Are you sure you want to generate a new map?<br>
alertMessage.innerHTML = /* html */ `Are you sure you want to generate a new map?<br />
All unsaved changes made to the current map will be lost`;
$("#alert").dialog({
resizable: false,
@ -752,13 +754,13 @@ async function showLoadPane() {
document.getElementById("loadFromDropboxSelect").style.display = "block";
const loadFromDropboxButtons = document.getElementById("loadFromDropboxButtons");
const fileSelect = document.getElementById("loadFromDropboxSelect");
fileSelect.innerHTML = `<option value="" disabled selected>Loading...</option>`;
fileSelect.innerHTML = /* html */ `<option value="" disabled selected>Loading...</option>`;
const files = await Cloud.providers.dropbox.list();
if (!files) {
loadFromDropboxButtons.style.display = "none";
fileSelect.innerHTML = `<option value="" disabled selected>Save files to Dropbox first</option>`;
fileSelect.innerHTML = /* html */ `<option value="" disabled selected>Save files to Dropbox first</option>`;
return;
}
@ -841,7 +843,7 @@ function openSaveTiles() {
loading = setInterval(() => (status.innerHTML += "."), 1000);
saveTiles().then(() => {
clearInterval(loading);
status.innerHTML = `Done. Check file in "Downloads" (crtl + J)`;
status.innerHTML = /* html */ `Done. Check file in "Downloads" (crtl + J)`;
setTimeout(() => (status.innerHTML = ""), 8000);
});
},
@ -874,7 +876,7 @@ function updateTilesOptions() {
const sizeY = graphHeight * scale * tilesY;
const totalSize = sizeX * sizeY;
tileSize.innerHTML = `${sizeX} x ${sizeY} px`;
tileSize.innerHTML = /* html */ `${sizeX} x ${sizeY} px`;
tileSize.style.color = totalSize > 1e9 ? "#d00b0b" : totalSize > 1e8 ? "#9e6409" : "#1a941a";
// draw tiles

View file

@ -131,25 +131,37 @@ function editProvinces() {
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 += `<div class="states" data-id=${p.i} data-name="${p.name}" data-form="${p.formName}" data-color="${
p.color
}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
lines += /* html */ `<div
class="states"
data-id=${p.i}
data-name="${p.name}"
data-form="${p.formName}"
data-color="${p.color}"
data-capital="${capital}"
data-state="${stateName}"
data-area=${area}
data-population=${population}
>
<fill-box fill="${p.color}"></fill-box>
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly>
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly />
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${p.i}"></use></svg>
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly>
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly />
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${p.burg ? "" : "placeholder"}"></span>
<select data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital" class="cultureBase hide ${
p.burgs.length ? "" : "placeholder"
}">${p.burgs.length ? getCapitalOptions(p.burgs, p.burg) : ""}</select>
<select
data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital"
class="cultureBase hide ${p.burgs.length ? "" : "placeholder"}"
>
${p.burgs.length ? getCapitalOptions(p.burgs, p.burg) : ""}
</select>
<input data-tip="Province owner" class="provinceOwner" value="${stateName}" disabled">
<span data-tip="Province area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Province area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<span data-tip="Declare province independence (turn non-capital province with burgs into a new state)" class="icon-flag-empty ${
separable ? "" : "placeholder"
} hide"></span>
<span
data-tip="Declare province independence (turn non-capital province with burgs into a new state)"
class="icon-flag-empty ${separable ? "" : "placeholder"} hide"
></span>
<span data-tip="Toggle province focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
<span data-tip="Remove the province" class="icon-trash-empty hide"></span>
</div>`;
@ -360,9 +372,8 @@ function editProvinces() {
const total = rural + urban;
const l = n => Number(n).toLocaleString();
alertMessage.innerHTML = `
Rural: <input type="number" min=0 step=1 id="ruralPop" value=${rural} style="width:6em">
Urban: <input type="number" min=0 step=1 id="urbanPop" value=${urban} style="width:6em" ${p.burgs.length ? "" : "disabled"}>
alertMessage.innerHTML = /* html */ ` Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${p.burgs.length ? "" : "disabled"} />
<p>Total population: ${l(total)} <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
const update = function () {
@ -424,7 +435,7 @@ function editProvinces() {
}
function removeProvince(p) {
alertMessage.innerHTML = `Are you sure you want to remove the province? <br>This action cannot be reverted`;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the province? <br />This action cannot be reverted`;
$("#alert").dialog({
resizable: false,
title: "Remove province",
@ -588,7 +599,7 @@ function editProvinces() {
const treeLayout = d3.treemap().size([w, h]).padding(2);
// prepare svg
alertMessage.innerHTML = `<select id="provincesTreeType" style="display:block; margin-left:13px; font-size:11px">
alertMessage.innerHTML = /* html */ `<select id="provincesTreeType" style="display:block; margin-left:13px; font-size:11px">
<option value="area" selected>Area</option>
<option value="population">Total population</option>
<option value="rural">Rural population</option>
@ -635,7 +646,7 @@ function editProvinces() {
? "Urban population: " + si(urban)
: "Population: " + si(rural + urban);
provinceInfo.innerHTML = `${name}. ${state}. ${value}`;
provinceInfo.innerHTML = /* html */ `${name}. ${state}. ${value}`;
provinceHighlightOn(ev);
}
@ -1027,7 +1038,7 @@ function editProvinces() {
}
function removeAllProvinces() {
alertMessage.innerHTML = `Are you sure you want to remove all provinces? <br>This action cannot be reverted`;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove all provinces? <br />This action cannot be reverted`;
$("#alert").dialog({
resizable: false,
title: "Remove all provinces",

View file

@ -60,11 +60,11 @@ function overviewRegiments(state) {
.map(u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name] || 0}</div>`)
.join(" ");
lines += `<div class="states" data-id=${r.i} data-s="${s.i}" data-state="${s.name}" data-name="${r.name}" ${sortData} data-total="${r.a}">
lines += /* html */ `<div class="states" data-id=${r.i} data-s="${s.i}" data-state="${s.name}" data-name="${r.name}" ${sortData} data-total="${r.a}">
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly />
<span data-tip="Regiment's emblem" style="width:1em">${r.icon}</span>
<input data-tip="Regiment's name" style="width:13em" value="${r.name}" readonly>
<input data-tip="Regiment's name" style="width:13em" value="${r.name}" readonly />
${lineData}
<div data-type="total" data-tip="Total military personnel (not considering crew)" style="font-weight: bold">${r.a}</div>
<span data-tip="Edit regiment" onclick="editRegiment('#regiment${s.i}-${r.i}')" class="icon-pencil pointer"></span>
@ -74,7 +74,7 @@ function overviewRegiments(state) {
}
}
lines += `<div id="regimentsTotalLine" class="totalLine" data-tip="Total of all displayed regiments">
lines += /* html */ `<div id="regimentsTotalLine" class="totalLine" data-tip="Total of all displayed regiments">
<div style="width: 21em; margin-left: 1em">Regiments: ${regiments.length}</div>
${options.military.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name] || 0)))}</div>`).join(" ")}
<div style="width:5em">${si(d3.sum(regiments.map(r => r.a)))}</div>

View file

@ -254,7 +254,7 @@ function editReliefIcon() {
let selection = null;
const pressed = reliefTools.querySelector("button.pressed");
if (pressed.id === "reliefIndividual") {
alertMessage.innerHTML = `Are you sure you want to remove the icon?`;
alertMessage.innerHTML = "Are you sure you want to remove the icon?";
selection = elSelected;
} else {
const type = reliefIconsDiv.querySelector("svg.pressed")?.dataset.type;

View file

@ -77,14 +77,26 @@ function editReligions() {
totalPopulation += population;
if (r.i) {
lines += `<div class="states religions" data-id=${r.i} data-name="${r.name}" data-color="${r.color}" data-area=${area}
data-population=${population} data-type=${r.type} data-form=${r.form} data-deity="${r.deity ? r.deity : ""}" data-expansionism=${r.expansionism}>
lines += /* html */ `<div
class="states religions"
data-id=${r.i}
data-name="${r.name}"
data-color="${r.color}"
data-area=${area}
data-population=${population}
data-type=${r.type}
data-form=${r.form}
data-deity="${r.deity ? r.deity : ""}"
data-expansionism=${r.expansionism}
>
<fill-box fill="${r.color}"></fill-box>
<input data-tip="Religion name. Click and type to change" class="religionName" value="${r.name}" autocorrect="off" spellcheck="false">
<select data-tip="Religion type" class="religionType">${getTypeOptions(r.type)}</select>
<input data-tip="Religion form" class="religionForm hide" value="${r.form}" autocorrect="off" spellcheck="false">
<input data-tip="Religion name. Click and type to change" class="religionName" value="${r.name}" autocorrect="off" spellcheck="false" />
<select data-tip="Religion type" class="religionType">
${getTypeOptions(r.type)}
</select>
<input data-tip="Religion form" class="religionForm hide" value="${r.form}" autocorrect="off" spellcheck="false" />
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw hide"></span>
<input data-tip="Religion supreme deity" class="religionDeity hide" value="${r.deity ? r.deity : ""}" autocorrect="off" spellcheck="false">
<input data-tip="Religion supreme deity" class="religionDeity hide" value="${r.deity ? r.deity : ""}" autocorrect="off" spellcheck="false" />
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Religion area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
@ -93,15 +105,26 @@ function editReligions() {
</div>`;
} else {
// No religion (neutral) line
lines += `<div class="states" data-id=${r.i} data-name="${
r.name
}" data-color="" data-area=${area} data-population=${population} data-type="" data-form="" data-deity="" data-expansionism="">
lines += /* html */ `<div
class="states"
data-id=${r.i}
data-name="${r.name}"
data-color=""
data-area=${area}
data-population=${population}
data-type=""
data-form=""
data-deity=""
data-expansionism=""
>
<svg width="9" height="9" class="placeholder"></svg>
<input data-tip="Religion name. Click and type to change" class="religionName italic" value="${r.name}" autocorrect="off" spellcheck="false">
<select data-tip="Religion type" class="religionType placeholder">${getTypeOptions(r.type)}</select>
<input data-tip="Religion form" class="religionForm placeholder hide" value="" autocorrect="off" spellcheck="false">
<input data-tip="Religion name. Click and type to change" class="religionName italic" value="${r.name}" autocorrect="off" spellcheck="false" />
<select data-tip="Religion type" class="religionType placeholder">
${getTypeOptions(r.type)}
</select>
<input data-tip="Religion form" class="religionForm placeholder hide" value="" autocorrect="off" spellcheck="false" />
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw placeholder hide"></span>
<input data-tip="Religion supreme deity" class="religionDeity placeholder hide" value="" autocorrect="off" spellcheck="false">
<input data-tip="Religion supreme deity" class="religionDeity placeholder hide" value="" autocorrect="off" spellcheck="false" />
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Religion area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
@ -163,7 +186,7 @@ function editReligions() {
const rural = r.rural * populationRate;
const urban = r.urban * populationRate * urbanization;
const population = rural + urban > 0 ? ". " + si(rn(rural + urban)) + " believers" : ". Extinct";
info.innerHTML = `${r.name}${type}${form}${population}`;
info.innerHTML = /* html */ `${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");
}
@ -279,10 +302,14 @@ function editReligions() {
const l = n => Number(n).toLocaleString();
const burgs = pack.burgs.filter(b => !b.removed && pack.cells.religion[b.cell] === religion);
alertMessage.innerHTML = `<p><i>Please note all population of religion territory is considered
believers of this religion. It means believers number change will directly change population</i></p>
Rural: <input type="number" min=0 step=1 id="ruralPop" value=${rural} style="width:6em">
Urban: <input type="number" min=0 step=1 id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"}>
alertMessage.innerHTML = /* html */ `<p>
<i
>Please note all population of religion territory is considered believers of this religion. It means believers number change will directly change
population</i
>
</p>
Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
<p>Total believers: ${l(total)} <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
const update = function () {

View file

@ -38,14 +38,23 @@ function overviewRivers() {
const width = rn(r.width * distanceScaleInput.value, 3) + " " + unit;
const basin = pack.rivers.find(river => river.i === r.basin)?.name;
lines += `<div class="states" data-id=${r.i} data-name="${r.name}" data-type="${r.type}" data-discharge="${r.discharge}" data-length="${r.length}" data-width="${r.width}" data-basin="${basin}">
lines += /* html */ `<div
class="states"
data-id=${r.i}
data-name="${r.name}"
data-type="${r.type}"
data-discharge="${r.discharge}"
data-length="${r.length}"
data-width="${r.width}"
data-basin="${basin}"
>
<span data-tip="Click to focus on river" class="icon-dot-circled pointer"></span>
<div data-tip="River name" class="riverName">${r.name}</div>
<div data-tip="River type name" class="riverType">${r.type}</div>
<div data-tip="River discharge (flux power)" class="biomeArea">${discharge}</div>
<div data-tip="River length from source to mouth" class="biomeArea">${length}</div>
<div data-tip="River mouth width" class="biomeArea">${width}</div>
<input data-tip="River basin (name of the main stem)" class="stateName" value="${basin}" disabled>
<input data-tip="River basin (name of the main stem)" class="stateName" value="${basin}" disabled />
<span data-tip="Edit river" class="icon-pencil"></span>
<span data-tip="Remove river" class="icon-trash-empty"></span>
</div>`;
@ -136,8 +145,7 @@ function overviewRivers() {
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`;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the river? All tributaries will be auto-removed`;
$("#alert").dialog({
resizable: false,
@ -157,7 +165,7 @@ function overviewRivers() {
}
function triggerAllRiversRemove() {
alertMessage.innerHTML = `Are you sure you want to remove all rivers?`;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove all rivers?`;
$("#alert").dialog({
resizable: false,
title: "Remove all rivers",

View file

@ -6,7 +6,8 @@ function editRoute(onClick) {
if (!layerIsOn("toggleRoutes")) toggleRoutes();
$("#routeEditor").dialog({
title: "Edit Route", resizable: false,
title: "Edit Route",
resizable: false,
position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"},
close: closeRoutesEditor
});
@ -41,14 +42,14 @@ function editRoute(onClick) {
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 (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]);
}
@ -56,8 +57,12 @@ function editRoute(onClick) {
}
function addControlPoint(point, before = null) {
debug.select("#controlPoints").insert("circle", before)
.attr("cx", point[0]).attr("cy", point[1]).attr("r", .6)
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);
}
@ -67,7 +72,7 @@ function editRoute(onClick) {
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();
}
@ -79,9 +84,12 @@ function editRoute(onClick) {
}
function redrawRoute() {
lineGen.curve(d3.curveCatmullRom.alpha(.1));
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
const points = [];
debug.select("#controlPoints").selectAll("circle").each(function() {
debug
.select("#controlPoints")
.selectAll("circle")
.each(function () {
points.push([this.getAttribute("cx"), this.getAttribute("cy")]);
});
@ -98,12 +106,12 @@ function editRoute(onClick) {
}
function showGroupSection() {
document.querySelectorAll("#routeEditor > button").forEach(el => el.style.display = "none");
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.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 = "";
@ -115,7 +123,7 @@ function editRoute(onClick) {
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));
});
}
@ -136,8 +144,14 @@ function editRoute(onClick) {
}
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");
@ -174,19 +188,28 @@ function editRoute(onClick) {
const group = elSelected.node().parentNode.id;
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"}?
<br><br>Routes to be removed: ${count}`;
$("#alert").dialog({resizable: false, title: "Remove route group",
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${
basic ? "all elements in the group" : "the entire route group"
}? <br /><br />Routes to be
removed: ${count}`;
$("#alert").dialog({
resizable: false,
title: "Remove route group",
buttons: {
Remove: function() {
Remove: function () {
$(this).dialog("close");
$("#routeEditor").dialog("close");
hideGroupSection();
if (basic) routes.select("#"+group).selectAll("path").remove();
else routes.select("#"+group).remove();
if (basic)
routes
.select("#" + group)
.selectAll("path")
.remove();
else routes.select("#" + group).remove();
},
Cancel: function() {$(this).dialog("close");}
Cancel: function () {
$(this).dialog("close");
}
}
});
}
@ -203,17 +226,24 @@ function editRoute(onClick) {
function clickControlPoint() {
if (routeSplit.classList.contains("pressed")) splitRoute(this);
else {this.remove(); redrawRoute();}
else {
this.remove();
redrawRoute();
}
}
function splitRoute(clicked) {
lineGen.curve(d3.curveCatmullRom.alpha(.1));
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
const group = d3.select(elSelected.node().parentNode);
routeSplit.classList.remove("pressed");
const points1 = [], points2 = [];
const points1 = [],
points2 = [];
let points = points1;
debug.select("#controlPoints").selectAll("circle").each(function() {
debug
.select("#controlPoints")
.selectAll("circle")
.each(function () {
points.push([this.getAttribute("cx"), this.getAttribute("cy")]);
if (this === clicked) {
points = points2;
@ -263,14 +293,18 @@ function editRoute(onClick) {
function removeRoute() {
alertMessage.innerHTML = "Are you sure you want to remove the route?";
$("#alert").dialog({resizable: false, title: "Remove route",
$("#alert").dialog({
resizable: false,
title: "Remove route",
buttons: {
Remove: function() {
Remove: function () {
$(this).dialog("close");
elSelected.remove();
$("#routeEditor").dialog("close");
},
Cancel: function() {$(this).dialog("close");}
Cancel: function () {
$(this).dialog("close");
}
}
});
}

View file

@ -100,24 +100,41 @@ function editStates() {
if (!s.i) {
// Neutral line
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-cells=${s.cells} data-area=${area}
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
lines += /* html */ `<div
class="states"
data-id=${s.i}
data-name="${s.name}"
data-cells=${s.cells}
data-area=${area}
data-population=${population}
data-burgs=${s.burgs}
data-color=""
data-form=""
data-capital=""
data-culture=""
data-type=""
data-expansionism=""
>
<svg width="1em" height="1em" class="placeholder"></svg>
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly>
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly />
<svg class="coaIcon placeholder hide"></svg>
<input class="stateForm placeholder" value="none">
<input class="stateForm placeholder" value="none" />
<span class="icon-star-empty placeholder hide"></span>
<input class="stateCapital placeholder hide">
<select class="stateCulture placeholder hide">${getCultureOptions(0)}</select>
<input class="stateCapital placeholder hide" />
<select class="stateCulture placeholder hide">
${getCultureOptions(0)}
</select>
<span data-tip="Burgs count" style="padding-right: 1px" class="icon-dot-circled hide"></span>
<div data-tip="Burgs count" class="stateBurgs hide">${s.burgs}</div>
<span data-tip="Neutral lands area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Neutral lands area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<select class="cultureType ${hidden} placeholder show hide">${getTypeOptions(0)}</select>
<select class="cultureType ${hidden} placeholder show hide">
${getTypeOptions(0)}
</select>
<span class="icon-resize-full ${hidden} placeholder show hide"></span>
<input class="statePower ${hidden} placeholder show hide" type="number" value=0>
<input class="statePower ${hidden} placeholder show hide" type="number" value="0" />
<span data-tip="Cells count" class="icon-check-empty ${hidden} show hide"></span>
<div data-tip="Cells count" class="stateCells ${hidden} show hide">${s.cells}</div>
</div>`;
@ -126,26 +143,49 @@ function editStates() {
const capital = pack.burgs[s.capital].name;
COArenderer.trigger("stateCOA" + s.i, s.coa);
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-form="${s.formName}" data-capital="${capital}"
data-color="${s.color}" data-cells=${s.cells} data-area=${area} data-population=${population} data-burgs=${s.burgs}
data-culture=${pack.cultures[s.culture].name} data-type=${s.type} data-expansionism=${s.expansionism}>
lines += /* html */ `<div
class="states"
data-id=${s.i}
data-name="${s.name}"
data-form="${s.formName}"
data-capital="${capital}"
data-color="${s.color}"
data-cells=${s.cells}
data-area=${area}
data-population=${population}
data-burgs=${s.burgs}
data-culture=${pack.cultures[s.culture].name}
data-type=${s.type}
data-expansionism=${s.expansionism}
>
<fill-box fill="${s.color}"></fill-box>
<input data-tip="State name. Click to change" class="stateName name pointer" value="${s.name}" readonly>
<input data-tip="State name. Click to change" class="stateName name pointer" value="${s.name}" readonly />
<svg data-tip="Click to show and edit state emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#stateCOA${s.i}"></use></svg>
<input data-tip="State form name. Click to change" class="stateForm name pointer" value="${s.formName}" readonly>
<input data-tip="State form name. Click to change" class="stateForm name pointer" value="${s.formName}" readonly />
<span data-tip="State capital. Click to zoom into view" class="icon-star-empty pointer hide"></span>
<input data-tip="Capital name. Click and type to rename" class="stateCapital hide" value="${capital}" autocorrect="off" spellcheck="false"/>
<select data-tip="Dominant culture. Click to change" class="stateCulture hide">${getCultureOptions(s.culture)}</select>
<input data-tip="Capital name. Click and type to rename" class="stateCapital hide" value="${capital}" autocorrect="off" spellcheck="false" />
<select data-tip="Dominant culture. Click to change" class="stateCulture hide">
${getCultureOptions(s.culture)}
</select>
<span data-tip="Burgs count" style="padding-right: 1px" class="icon-dot-circled hide"></span>
<div data-tip="Burgs count" class="stateBurgs hide">${s.burgs}</div>
<span data-tip="State area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="State area" class="biomeArea hide">${si(area) + unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<select data-tip="State type. Defines growth model. Click to change" class="cultureType ${hidden} show hide">${getTypeOptions(s.type)}</select>
<select data-tip="State type. Defines growth model. Click to change" class="cultureType ${hidden} show hide">
${getTypeOptions(s.type)}
</select>
<span data-tip="State expansionism" class="icon-resize-full ${hidden} show hide"></span>
<input data-tip="Expansionism (defines competitive size). Change to re-calculate states based on new value"
class="statePower ${hidden} show hide" type="number" min=0 max=99 step=.1 value=${s.expansionism}>
<input
data-tip="Expansionism (defines competitive size). Change to re-calculate states based on new value"
class="statePower ${hidden} show hide"
type="number"
min="0"
max="99"
step=".1"
value=${s.expansionism}
/>
<span data-tip="Cells count" class="icon-check-empty ${hidden} show hide"></span>
<div data-tip="Cells count" class="stateCells ${hidden} show hide">${s.cells}</div>
<span data-tip="Toggle state focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
@ -373,9 +413,8 @@ function editStates() {
const total = rural + urban;
const l = n => Number(n).toLocaleString();
alertMessage.innerHTML = `
Rural: <input type="number" min=0 step=1 id="ruralPop" value=${rural} style="width:6em">
Urban: <input type="number" min=0 step=1 id="urbanPop" value=${urban} style="width:6em" ${s.burgs ? "" : "disabled"}>
alertMessage.innerHTML = /* html */ ` Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${s.burgs ? "" : "disabled"} />
<p>Total population: ${l(total)} <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
const update = function () {
@ -592,7 +631,7 @@ function editStates() {
const treeLayout = d3.pack().size([w, h]).padding(3);
// prepare svg
alertMessage.innerHTML = `<select id="statesTreeType" style="display:block; margin-left:13px; font-size:11px">
alertMessage.innerHTML = /* html */ `<select id="statesTreeType" style="display:block; margin-left:13px; font-size:11px">
<option value="area" selected>Area</option>
<option value="population">Total population</option>
<option value="rural">Rural population</option>
@ -664,7 +703,7 @@ function editStates() {
? "Burgs number: " + d.data.burgs
: "Population: " + si(rural + urban);
statesInfo.innerHTML = `${state}. ${value}`;
statesInfo.innerHTML = /* html */ `${state}. ${value}`;
stateHighlightOn(ev);
}

View file

@ -712,8 +712,8 @@ emblemsBurgSizeInput.addEventListener("change", 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:
<input id="textureURL" type="url" style="width: 100%" placeholder="http://www.example.com/image.jpg" oninput="fetchTextureURL(this.value)">
alertMessage.innerHTML = /* html */ `Provide an image URL to be used as a texture:
<input id="textureURL" type="url" style="width: 100%" placeholder="http://www.example.com/image.jpg" oninput="fetchTextureURL(this.value)" />
<canvas id="texturePreview" width="256px" height="144px"></canvas>`;
$("#alert").dialog({
resizable: false,

View file

@ -203,8 +203,7 @@ window.UISubmap = (function () {
ERROR && console.error(error);
clearMainTip();
alertMessage.innerHTML = `Map resampling failed:
<br>You may retry after clearing stored data or contact us at discord.
alertMessage.innerHTML = /* html */ `Map resampling failed: <br />You may retry after clearing stored data or contact us at discord.
<p id="errorBox">${parseError(error)}</p>`;
$("#alert").dialog({
resizable: false,

View file

@ -35,7 +35,7 @@ toolsContent.addEventListener("click", function (event) {
return;
}
alertMessage.innerHTML = `Regeneration will remove all the custom changes for the element.<br><br>Are you sure you want to proceed?`;
alertMessage.innerHTML = /* html */ `Regeneration will remove all the custom changes for the element.<br /><br />Are you sure you want to proceed?`;
$("#alert").dialog({
resizable: false,
title: "Regenerate element",

View file

@ -277,9 +277,8 @@ function editUnits() {
function removeAllRulers() {
if (!rulers.data.length) return;
alertMessage.innerHTML = `
Are you sure you want to remove all placed rulers?
<br>If you just want to hide rulers, toggle the Rulers layer off in Menu`;
alertMessage.innerHTML = /* html */ ` Are you sure you want to remove all placed rulers?
<br />If you just want to hide rulers, toggle the Rulers layer off in Menu`;
$("#alert").dialog({
resizable: false,
title: "Remove all rulers",

View file

@ -77,12 +77,12 @@ function editWorld() {
const scale = +distanceScaleInput.value,
unit = distanceUnitInput.value;
const meridian = toKilometer(eqD * 2 * scale);
document.getElementById("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
document.getElementById("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
document.getElementById("mapSize").innerHTML = /* html */ `${graphWidth}x${graphHeight}`;
document.getElementById("mapSizeFriendly").innerHTML = /* html */ `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
document.getElementById("meridianLength").innerHTML = rn(eqD * 2);
document.getElementById("meridianLengthFriendly").innerHTML = `${rn(eqD * 2 * scale)} ${unit}`;
document.getElementById("meridianLengthFriendly").innerHTML = /* html */ `${rn(eqD * 2 * scale)} ${unit}`;
document.getElementById("meridianLengthEarth").innerHTML = meridian ? " = " + rn(meridian / 200) + "%🌏" : "";
document.getElementById("mapCoordinates").innerHTML = `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(mc.latS)} ${rn(mc.lonE)}°E`;
document.getElementById("mapCoordinates").innerHTML = /* html */ `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(mc.latS)} ${rn(mc.lonE)}°E`;
function toKilometer(v) {
if (unit === "km") return v;

View file

@ -110,7 +110,7 @@ function editZones() {
const totalArea = (zonesFooterArea.dataset.area = graphWidth * graphHeight * distanceScaleInput.value ** 2);
const totalPop = (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * populationRate;
zonesFooterPopulation.dataset.population = totalPop;
zonesFooterNumber.innerHTML = `${filteredZones.length} of ${zones.length}`;
zonesFooterNumber.innerHTML = /* html */ `${filteredZones.length} of ${zones.length}`;
zonesFooterCells.innerHTML = pack.cells.i.length;
zonesFooterArea.innerHTML = si(totalArea) + unit;
zonesFooterPopulation.innerHTML = si(totalPop);
@ -414,9 +414,8 @@ function editZones() {
const total = rural + urban;
const l = n => Number(n).toLocaleString();
alertMessage.innerHTML = `
Rural: <input type="number" min=0 step=1 id="ruralPop" value=${rural} style="width:6em">
Urban: <input type="number" min=0 step=1 id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"}>
alertMessage.innerHTML = /* html */ `Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
<p>Total population: ${l(total)} <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
const update = function () {