diff --git a/index.html b/index.html index 19014b96..2a5e9214 100644 --- a/index.html +++ b/index.html @@ -6096,6 +6096,7 @@ + diff --git a/modules/dynamic/editors/cultures-editor.js b/modules/dynamic/editors/cultures-editor.js index fddb8bb5..7ef8f1e3 100644 --- a/modules/dynamic/editors/cultures-editor.js +++ b/modules/dynamic/editors/cultures-editor.js @@ -1,4 +1,4 @@ -const body = insertEditorHtml(); +const $body = insertEditorHtml(); addListeners(); const cultureTypes = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; @@ -20,7 +20,7 @@ export function open() { close: closeCulturesEditor, position: {my: "right top", at: "right-10 top+10", of: "svg"} }); - body.focus(); + $body.focus(); } function insertEditorHtml() { @@ -85,29 +85,27 @@ function insertEditorHtml() { `; - const dialogs = document.getElementById("dialogs"); - dialogs.insertAdjacentHTML("beforeend", editorHtml); - - return document.getElementById("culturesBody"); + byId("dialogs").insertAdjacentHTML("beforeend", editorHtml); + return byId("culturesBody"); } function addListeners() { applySortingByHeader("culturesHeader"); - document.getElementById("culturesEditorRefresh").addEventListener("click", refreshCulturesEditor); - document.getElementById("culturesEditStyle").addEventListener("click", () => editStyle("cults")); - document.getElementById("culturesLegend").addEventListener("click", toggleLegend); - document.getElementById("culturesPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("culturesHeirarchy").addEventListener("click", showHierarchy); - document.getElementById("culturesRecalculate").addEventListener("click", () => recalculateCultures(true)); - document.getElementById("culturesManually").addEventListener("click", enterCultureManualAssignent); - document.getElementById("culturesManuallyApply").addEventListener("click", applyCultureManualAssignent); - document.getElementById("culturesManuallyCancel").addEventListener("click", () => exitCulturesManualAssignment()); - document.getElementById("culturesEditNamesBase").addEventListener("click", editNamesbase); - document.getElementById("culturesAdd").addEventListener("click", enterAddCulturesMode); - document.getElementById("culturesExport").addEventListener("click", downloadCulturesData); - document.getElementById("culturesImport").addEventListener("click", () => document.getElementById("culturesCSVToLoad").click()); - document.getElementById("culturesCSVToLoad").addEventListener("change", uploadCulturesData); + byId("culturesEditorRefresh").on("click", refreshCulturesEditor); + byId("culturesEditStyle").on("click", () => editStyle("cults")); + byId("culturesLegend").on("click", toggleLegend); + byId("culturesPercentage").on("click", togglePercentageMode); + byId("culturesHeirarchy").on("click", showHierarchy); + byId("culturesRecalculate").on("click", () => recalculateCultures(true)); + byId("culturesManually").on("click", enterCultureManualAssignent); + byId("culturesManuallyApply").on("click", applyCultureManualAssignent); + byId("culturesManuallyCancel").on("click", () => exitCulturesManualAssignment()); + byId("culturesEditNamesBase").on("click", editNamesbase); + byId("culturesAdd").on("click", enterAddCulturesMode); + byId("culturesExport").on("click", downloadCulturesCsv); + byId("culturesImport").on("click", () => byId("culturesCSVToLoad").click()); + byId("culturesCSVToLoad").on("change", uploadCulturesData); } function refreshCulturesEditor() { @@ -137,7 +135,7 @@ function culturesEditorAddLines() { let totalArea = 0; let totalPopulation = 0; - const emblemShapeGroup = document.getElementById("emblemShape")?.selectedOptions[0]?.parentNode?.label; + const emblemShapeGroup = byId("emblemShape")?.selectedOptions[0]?.parentNode?.label; const selectShape = emblemShapeGroup === "Diversiform"; for (const c of pack.cultures) { @@ -225,38 +223,39 @@ function culturesEditorAddLines() { `; } - body.innerHTML = lines; + $body.innerHTML = lines; // update footer - document.getElementById("culturesFooterCultures").innerHTML = pack.cultures.filter(c => c.i && !c.removed).length; - document.getElementById("culturesFooterCells").innerHTML = pack.cells.h.filter(h => h >= 20).length; - document.getElementById("culturesFooterArea").innerHTML = `${si(totalArea)} ${unit}`; - document.getElementById("culturesFooterPopulation").innerHTML = si(totalPopulation); - document.getElementById("culturesFooterArea").dataset.area = totalArea; - document.getElementById("culturesFooterPopulation").dataset.population = totalPopulation; + byId("culturesFooterCultures").innerHTML = pack.cultures.filter(c => c.i && !c.removed).length; + byId("culturesFooterCells").innerHTML = pack.cells.h.filter(h => h >= 20).length; + byId("culturesFooterArea").innerHTML = `${si(totalArea)} ${unit}`; + byId("culturesFooterPopulation").innerHTML = si(totalPopulation); + byId("culturesFooterArea").dataset.area = totalArea; + byId("culturesFooterPopulation").dataset.population = totalPopulation; // add listeners - body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseenter", cultureHighlightOn)); - body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseleave", cultureHighlightOff)); - body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectCultureOnLineClick)); - body.querySelectorAll("fill-box").forEach(el => el.addEventListener("click", cultureChangeColor)); - body.querySelectorAll("div > input.cultureName").forEach(el => el.addEventListener("input", cultureChangeName)); - body.querySelectorAll("div > span.icon-cw").forEach(el => el.addEventListener("click", cultureRegenerateName)); - body.querySelectorAll("div > input.cultureExpan").forEach(el => el.addEventListener("input", cultureChangeExpansionism)); - body.querySelectorAll("div > select.cultureType").forEach(el => el.addEventListener("change", cultureChangeType)); - body.querySelectorAll("div > select.cultureBase").forEach(el => el.addEventListener("change", cultureChangeBase)); - body.querySelectorAll("div > select.cultureEmblems").forEach(el => el.addEventListener("change", cultureChangeEmblemsShape)); - body.querySelectorAll("div > div.culturePopulation").forEach(el => el.addEventListener("click", changePopulation)); - body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.addEventListener("click", cultureRegenerateBurgs)); - body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", cultureRemove)); + $body.querySelectorAll("div.cultures").forEach(el => el.on("mouseenter", cultureHighlightOn)); + $body.querySelectorAll("div.cultures").forEach(el => el.on("mouseleave", cultureHighlightOff)); + $body.querySelectorAll("div.states").forEach(el => el.on("click", selectCultureOnLineClick)); + $body.querySelectorAll("fill-box").forEach(el => el.on("click", cultureChangeColor)); + $body.querySelectorAll("div > input.cultureName").forEach(el => el.on("input", cultureChangeName)); + $body.querySelectorAll("div > span.icon-cw").forEach(el => el.on("click", cultureRegenerateName)); + $body.querySelectorAll("div > input.cultureExpan").forEach(el => el.on("input", cultureChangeExpansionism)); + $body.querySelectorAll("div > select.cultureType").forEach(el => el.on("change", cultureChangeType)); + $body.querySelectorAll("div > select.cultureBase").forEach(el => el.on("change", cultureChangeBase)); + $body.querySelectorAll("div > select.cultureEmblems").forEach(el => el.on("change", cultureChangeEmblemsShape)); + $body.querySelectorAll("div > div.culturePopulation").forEach(el => el.on("click", changePopulation)); + $body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.on("click", cultureRegenerateBurgs)); + $body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.on("click", cultureRemove)); - culturesHeader.querySelector("div[data-sortby='emblems']").style.display = selectShape ? "inline-block" : "none"; + const $culturesHeader = byId("culturesHeader"); + $culturesHeader.querySelector("div[data-sortby='emblems']").style.display = selectShape ? "inline-block" : "none"; - if (body.dataset.type === "percentage") { - body.dataset.type = "absolute"; + if ($body.dataset.type === "percentage") { + $body.dataset.type = "absolute"; togglePercentageMode(); } - applySorting(culturesHeader); + applySorting($culturesHeader); $("#culturesEditor").dialog({width: fitContent()}); } @@ -284,8 +283,8 @@ function getShapeOptions(selectShape, selected) { function cultureHighlightOn(event) { const culture = +event.target.dataset.id; - const info = document.getElementById("cultureInfo"); - if (info) { + const $info = byId("cultureInfo"); + if ($info) { d3.select("#hierarchy") .select("g[data-id='" + culture + "'] > path") .classed("selected", 1); @@ -293,7 +292,7 @@ function cultureHighlightOn(event) { 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 = `${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"); } @@ -317,12 +316,12 @@ function cultureHighlightOn(event) { function cultureHighlightOff(event) { const culture = +event.target.dataset.id; - const info = document.getElementById("cultureInfo"); - if (info) { + const $info = byId("cultureInfo"); + if ($info) { d3.select("#hierarchy") .select("g[data-id='" + culture + "'] > path") .classed("selected", 0); - info.innerHTML = "‍"; + $info.innerHTML = "‍"; tip(""); } @@ -340,12 +339,12 @@ function cultureHighlightOff(event) { } function cultureChangeColor() { - const el = this; - const currentFill = el.getAttribute("fill"); - const culture = +el.parentNode.dataset.id; + const $el = this; + const currentFill = $el.getAttribute("fill"); + const culture = +$el.parentNode.dataset.id; const callback = newFill => { - el.fill = newFill; + $el.fill = newFill; pack.cultures[culture].color = newFill; cults .select("#culture" + culture) @@ -400,9 +399,9 @@ function cultureChangeEmblemsShape() { this.parentNode.dataset.emblems = pack.cultures[culture].shield = shape; const rerenderCOA = (id, coa) => { - const coaEl = document.getElementById(id); - if (!coaEl) return; // not rendered - coaEl.remove(); + const $coa = byId(id); + if (!$coa) return; // not rendered + $coa.remove(); COArenderer.trigger(id, coa); }; @@ -570,12 +569,12 @@ function drawCultureCenters() { .attr("cy", d => pack.cells.p[d.center][1]) .on("mouseenter", d => { tip(tooltip, true); - body.querySelector(`div[data-id='${d.i}']`).classList.add("selected"); + $body.querySelector(`div[data-id='${d.i}']`).classList.add("selected"); cultureHighlightOn(event); }) .on("mouseleave", d => { tip("", true); - body.querySelector(`div[data-id='${d.i}']`).classList.remove("selected"); + $body.querySelector(`div[data-id='${d.i}']`).classList.remove("selected"); cultureHighlightOff(event); }) .call(d3.drag().on("start", cultureCenterDrag)); @@ -606,19 +605,19 @@ function toggleLegend() { } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; + if ($body.dataset.type === "absolute") { + $body.dataset.type = "percentage"; const totalCells = +culturesFooterCells.innerHTML; const totalArea = +culturesFooterArea.dataset.area; const totalPopulation = +culturesFooterPopulation.dataset.population; - body.querySelectorAll(":scope > div").forEach(function (el) { + $body.querySelectorAll(":scope > div").forEach(function (el) { el.querySelector(".cultureCells").innerHTML = rn((+el.dataset.cells / totalCells) * 100) + "%"; el.querySelector(".cultureArea").innerHTML = rn((+el.dataset.area / totalArea) * 100) + "%"; el.querySelector(".culturePopulation").innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + "%"; }); } else { - body.dataset.type = "absolute"; + $body.dataset.type = "absolute"; culturesEditorAddLines(); } } @@ -790,12 +789,12 @@ function enterCultureManualAssignent() { customization = 4; cults.append("g").attr("id", "temp"); document.querySelectorAll("#culturesBottom > *").forEach(el => (el.style.display = "none")); - document.getElementById("culturesManuallyButtons").style.display = "inline-block"; + byId("culturesManuallyButtons").style.display = "inline-block"; debug.select("#cultureCenters").style("display", "none"); culturesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); culturesFooter.style.display = "none"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); $("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}}); tip("Click on culture to select, drag the circle to change culture", true); @@ -805,12 +804,12 @@ function enterCultureManualAssignent() { .call(d3.drag().on("start", dragCultureBrush)) .on("touchmove mousemove", moveCultureBrush); - body.querySelector("div").classList.add("selected"); + $body.querySelector("div").classList.add("selected"); } function selectCultureOnLineClick(i) { if (customization !== 4) return; - body.querySelector("div.selected").classList.remove("selected"); + $body.querySelector("div.selected").classList.remove("selected"); this.classList.add("selected"); } @@ -822,8 +821,8 @@ function selectCultureOnMapClick() { const assigned = cults.select("#temp").select("polygon[data-cell='" + i + "']"); const culture = assigned.size() ? +assigned.attr("data-culture") : pack.cells.culture[i]; - body.querySelector("div.selected").classList.remove("selected"); - body.querySelector("div[data-id='" + culture + "']").classList.add("selected"); + $body.querySelector("div.selected").classList.remove("selected"); + $body.querySelector("div[data-id='" + culture + "']").classList.add("selected"); } function dragCultureBrush() { @@ -842,7 +841,7 @@ function dragCultureBrush() { function changeCultureForSelection(selection) { const temp = cults.select("#temp"); - const selected = body.querySelector("div.selected"); + const selected = $body.querySelector("div.selected"); const cultureNew = +selected.dataset.id; const color = pack.cultures[cultureNew].color || "#ffffff"; @@ -887,17 +886,17 @@ function exitCulturesManualAssignment(close) { cults.select("#temp").remove(); removeCircle(); document.querySelectorAll("#culturesBottom > *").forEach(el => (el.style.display = "inline-block")); - document.getElementById("culturesManuallyButtons").style.display = "none"; + byId("culturesManuallyButtons").style.display = "none"; culturesEditor.querySelectorAll(".hide").forEach(el => el.classList.remove("hidden")); culturesFooter.style.display = "block"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); if (!close) $("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}}); debug.select("#cultureCenters").style("display", null); restoreDefaultEvents(); clearMainTip(); - const selected = body.querySelector("div.selected"); + const selected = $body.querySelector("div.selected"); if (selected) selected.classList.remove("selected"); } @@ -908,14 +907,14 @@ function enterAddCulturesMode() { this.classList.add("pressed"); tip("Click on the map to add a new culture", true); viewbox.style("cursor", "crosshair").on("click", addCulture); - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); } function exitAddCultureMode() { customization = 0; restoreDefaultEvents(); clearMainTip(); - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); if (culturesAdd.classList.contains("pressed")) culturesAdd.classList.remove("pressed"); } @@ -934,26 +933,20 @@ function addCulture() { culturesEditorAddLines(); } -function downloadCulturesData() { - let data = `Id,Culture,Color,Cells,Expansionism,Type,Area ${getAreaUnit("2")},Population,Namesbase,Emblems Shape,Origin\n`; // headers - - body.querySelectorAll(":scope > div").forEach(function (el) { - data += el.dataset.id + ","; - data += el.dataset.name + ","; - data += el.dataset.color + ","; - data += el.dataset.cells + ","; - data += el.dataset.expansionism + ","; - data += el.dataset.type + ","; - data += el.dataset.area + ","; - data += el.dataset.population + ","; - const base = +el.dataset.base; - data += nameBases[base].name + ","; - data += el.dataset.emblems + ","; - data += pack.cultures[+el.dataset.id].origin + "\n"; +function downloadCulturesCsv() { + const unit = getAreaUnit("2"); + const headers = `Id,Name,Color,Cells,Expansionism,Type,Area ${unit},Population,Namesbase,Emblems Shape,Origin`; + const lines = Array.from($body.querySelectorAll(":scope > div")); + const data = lines.map($line => { + const {id, name, color, cells, expansionism, type, area, population, emblems, base} = $line.dataset; + const namesbase = nameBases[+base].name; + const {origin} = pack.cultures[+id]; + return [id, name, color, cells, expansionism, type, area, population, namesbase, emblems, origin].join(","); }); + const csvData = [headers].concat(data).join("\n"); const name = getFileName("Cultures") + ".csv"; - downloadFile(data, name); + downloadFile(csvData, name); } function closeCulturesEditor() { diff --git a/modules/dynamic/editors/states-editor.js b/modules/dynamic/editors/states-editor.js index 28aa0f7c..ea97aec0 100644 --- a/modules/dynamic/editors/states-editor.js +++ b/modules/dynamic/editors/states-editor.js @@ -1,4 +1,4 @@ -const body = insertEditorHtml(); +const $body = insertEditorHtml(); addListeners(); export function open() { @@ -119,38 +119,35 @@ function insertEditorHtml() { `; - const dialogs = document.getElementById("dialogs"); - dialogs.insertAdjacentHTML("beforeend", editorHtml); - - return document.getElementById("statesBodySection"); + byId("dialogs").insertAdjacentHTML("beforeend", editorHtml); + return byId("statesBodySection"); } function addListeners() { applySortingByHeader("statesHeader"); - document.getElementById("statesEditorRefresh").addEventListener("click", refreshStatesEditor); - document.getElementById("statesEditStyle").addEventListener("click", () => editStyle("regions")); - document.getElementById("statesLegend").addEventListener("click", toggleLegend); - document.getElementById("statesPercentage").addEventListener("click", togglePercentageMode); - document.getElementById("statesChart").addEventListener("click", showStatesChart); - document.getElementById("statesRegenerate").addEventListener("click", openRegenerationMenu); - document.getElementById("statesRegenerateBack").addEventListener("click", exitRegenerationMenu); - document.getElementById("statesRecalculate").addEventListener("click", () => recalculateStates(true)); - document.getElementById("statesRandomize").addEventListener("click", randomizeStatesExpansion); - document.getElementById("statesNeutral").addEventListener("input", changeStatesGrowthRate); - document.getElementById("statesNeutralNumber").addEventListener("change", changeStatesGrowthRate); - document.getElementById("statesManually").addEventListener("click", enterStatesManualAssignent); - document.getElementById("statesManuallyApply").addEventListener("click", applyStatesManualAssignent); - document.getElementById("statesManuallyCancel").addEventListener("click", () => exitStatesManualAssignment()); - document.getElementById("statesAdd").addEventListener("click", enterAddStateMode); - document.getElementById("statesExport").addEventListener("click", downloadStatesData); + byId("statesEditorRefresh").on("click", refreshStatesEditor); + byId("statesEditStyle").on("click", () => editStyle("regions")); + byId("statesLegend").on("click", toggleLegend); + byId("statesPercentage").on("click", togglePercentageMode); + byId("statesChart").on("click", showStatesChart); + byId("statesRegenerate").on("click", openRegenerationMenu); + byId("statesRegenerateBack").on("click", exitRegenerationMenu); + byId("statesRecalculate").on("click", () => recalculateStates(true)); + byId("statesRandomize").on("click", randomizeStatesExpansion); + byId("statesNeutral").on("input", changeStatesGrowthRate); + byId("statesNeutralNumber").on("change", changeStatesGrowthRate); + byId("statesManually").on("click", enterStatesManualAssignent); + byId("statesManuallyApply").on("click", applyStatesManualAssignent); + byId("statesManuallyCancel").on("click", () => exitStatesManualAssignment(false)); + byId("statesAdd").on("click", enterAddStateMode); + byId("statesExport").on("click", downloadStatesCsv); - body.addEventListener("click", function (event) { - const element = event.target; - const classList = element.classList; - const line = element.parentNode; - const state = +line.dataset.id; - if (element.tagName === "FILL-BOX") stateChangeFill(element); + $body.on("click", event => { + const $element = event.target; + const classList = $element.classList; + const state = +$element.parentNode?.dataset?.id; + if ($element.tagName === "FILL-BOX") stateChangeFill($element); else if (classList.contains("name")) editStateName(state); else if (classList.contains("coaIcon")) editEmblem("state", "stateCOA" + state, pack.states[state]); else if (classList.contains("icon-star-empty")) stateCapitalZoomIn(state); @@ -159,22 +156,22 @@ function addListeners() { else if (classList.contains("icon-trash-empty")) stateRemovePrompt(state); }); - body.addEventListener("input", function (ev) { - const element = ev.target; - const classList = element.classList; - const line = element.parentNode; + $body.on("input", function (ev) { + const $element = ev.target; + const classList = $element.classList; + const line = $element.parentNode; const state = +line.dataset.id; - if (classList.contains("stateCapital")) stateChangeCapitalName(state, line, element.value); - else if (classList.contains("cultureType")) stateChangeType(state, line, element.value); - else if (classList.contains("statePower")) stateChangeExpansionism(state, line, element.value); + if (classList.contains("stateCapital")) stateChangeCapitalName(state, line, $element.value); + else if (classList.contains("cultureType")) stateChangeType(state, line, $element.value); + else if (classList.contains("statePower")) stateChangeExpansionism(state, line, $element.value); }); - body.addEventListener("change", function (ev) { - const element = ev.target; - const classList = element.classList; - const line = element.parentNode; + $body.on("change", function (ev) { + const $element = ev.target; + const classList = $element.classList; + const line = $element.parentNode; const state = +line.dataset.id; - if (classList.contains("stateCulture")) stateChangeCulture(state, line, element.value); + if (classList.contains("stateCulture")) stateChangeCulture(state, line, $element.value); }); } @@ -186,11 +183,11 @@ function refreshStatesEditor() { // add line for each state function statesEditorAddLines() { const unit = getAreaUnit(); - const hidden = statesRegenerateButtons.style.display === "block" ? "" : "hidden"; // show/hide regenerate columns - let lines = "", - totalArea = 0, - totalPopulation = 0, - totalBurgs = 0; + const hidden = byId("statesRegenerateButtons").style.display === "block" ? "" : "hidden"; // toggle regenerate columns + let lines = ""; + let totalArea = 0; + let totalPopulation = 0; + let totalBurgs = 0; for (const s of pack.states) { if (s.removed) continue; @@ -283,25 +280,26 @@ function statesEditorAddLines() { `; } - body.innerHTML = lines; + $body.innerHTML = lines; // update footer - statesFooterStates.innerHTML = pack.states.filter(s => s.i && !s.removed).length; - statesFooterCells.innerHTML = pack.cells.h.filter(h => h >= 20).length; - statesFooterBurgs.innerHTML = totalBurgs; - statesFooterArea.innerHTML = `${si(totalArea)} ${unit}`; - statesFooterPopulation.innerHTML = si(totalPopulation); - statesFooterArea.dataset.area = totalArea; - statesFooterPopulation.dataset.population = totalPopulation; + byId("statesFooterStates").innerHTML = pack.states.filter(s => s.i && !s.removed).length; + byId("statesFooterCells").innerHTML = pack.cells.h.filter(h => h >= 20).length; + byId("statesFooterBurgs").innerHTML = totalBurgs; + byId("statesFooterArea").innerHTML = si(totalArea) + unit; + byId("statesFooterArea").dataset.area = totalArea; + byId("statesFooterPopulation").innerHTML = si(totalPopulation); + byId("statesFooterPopulation").dataset.population = totalPopulation; - body.querySelectorAll("div.states").forEach(el => { - el.addEventListener("click", selectStateOnLineClick); - el.addEventListener("mouseenter", ev => stateHighlightOn(ev)); - el.addEventListener("mouseleave", ev => stateHighlightOff(ev)); + // add listeners + $body.querySelectorAll("div.states").forEach(el => { + el.on("click", selectStateOnLineClick); + el.on("mouseenter", stateHighlightOn); + el.on("mouseleave", stateHighlightOff); }); - if (body.dataset.type === "percentage") { - body.dataset.type = "absolute"; + if ($body.dataset.type === "percentage") { + $body.dataset.type = "absolute"; togglePercentageMode(); } applySorting(statesHeader); @@ -343,15 +341,13 @@ function stateHighlightOn(event) { .attr("opacity", 1) .attr("filter", "url(#blur1)"); - const l = path.node().getTotalLength(), - dur = (l + 5000) / 2; - const i = d3.interpolateString("0," + l, l + "," + l); + const totalLength = path.node().getTotalLength(); + const duration = (totalLength + 5000) / 2; + const interpolate = d3.interpolateString(`0, ${totalLength}`, `${totalLength}, ${totalLength}`); path .transition() - .duration(dur) - .attrTween("stroke-dasharray", function () { - return t => i(t); - }); + .duration(duration) + .attrTween("stroke-dasharray", () => interpolate); } function stateHighlightOff() { @@ -395,10 +391,10 @@ function editStateName(state) { } const s = pack.states[state]; - document.getElementById("stateNameEditor").dataset.state = state; - document.getElementById("stateNameEditorShort").value = s.name || ""; + byId("stateNameEditor").dataset.state = state; + byId("stateNameEditorShort").value = s.name || ""; applyOption(stateNameEditorSelectForm, s.formName); - document.getElementById("stateNameEditorFull").value = s.fullName || ""; + byId("stateNameEditorFull").value = s.fullName || ""; $("#stateNameEditor").dialog({ resizable: false, @@ -419,23 +415,23 @@ function editStateName(state) { modules.editStateName = true; // add listeners - document.getElementById("stateNameEditorShortCulture").addEventListener("click", regenerateShortNameCuture); - document.getElementById("stateNameEditorShortRandom").addEventListener("click", regenerateShortNameRandom); - document.getElementById("stateNameEditorAddForm").addEventListener("click", addCustomForm); - document.getElementById("stateNameEditorCustomForm").addEventListener("change", addCustomForm); - document.getElementById("stateNameEditorFullRegenerate").addEventListener("click", regenerateFullName); + byId("stateNameEditorShortCulture").on("click", regenerateShortNameCuture); + byId("stateNameEditorShortRandom").on("click", regenerateShortNameRandom); + byId("stateNameEditorAddForm").on("click", addCustomForm); + byId("stateNameEditorCustomForm").on("change", addCustomForm); + byId("stateNameEditorFullRegenerate").on("click", regenerateFullName); function regenerateShortNameCuture() { const state = +stateNameEditor.dataset.state; const culture = pack.states[state].culture; const name = Names.getState(Names.getCultureShort(culture), culture); - document.getElementById("stateNameEditorShort").value = name; + byId("stateNameEditorShort").value = name; } function regenerateShortNameRandom() { const base = rand(nameBases.length - 1); const name = Names.getState(Names.getBase(base), undefined, base); - document.getElementById("stateNameEditorShort").value = name; + byId("stateNameEditorShort").value = name; } function addCustomForm() { @@ -448,9 +444,9 @@ function editStateName(state) { } function regenerateFullName() { - const short = document.getElementById("stateNameEditorShort").value; - const form = document.getElementById("stateNameEditorSelectForm").value; - document.getElementById("stateNameEditorFull").value = getFullName(); + const short = byId("stateNameEditorShort").value; + const form = byId("stateNameEditorSelectForm").value; + byId("stateNameEditorFull").value = getFullName(); function getFullName() { if (!form) return short; @@ -462,9 +458,9 @@ function editStateName(state) { } function applyNameChange(s) { - const nameInput = document.getElementById("stateNameEditorShort"); - const formSelect = document.getElementById("stateNameEditorSelectForm"); - const fullNameInput = document.getElementById("stateNameEditorFull"); + const nameInput = byId("stateNameEditorShort"); + const formSelect = byId("stateNameEditorSelectForm"); + const fullNameInput = byId("stateNameEditorFull"); const nameChanged = nameInput.value !== s.name; const formChanged = formSelect.value !== s.formName; @@ -629,7 +625,7 @@ function stateRemove(state) { // remove emblem const coaId = "stateCOA" + state; - document.getElementById(coaId).remove(); + byId(coaId).remove(); emblems.select(`#stateEmblems > use[data-i='${state}']`).remove(); // remove provinces @@ -640,7 +636,7 @@ function stateRemove(state) { }); const coaId = "provinceCOA" + p; - if (document.getElementById(coaId)) document.getElementById(coaId).remove(); + if (byId(coaId)) byId(coaId).remove(); emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove(); const g = provs.select("#provincesBody"); g.select("#province" + p).remove(); @@ -684,21 +680,21 @@ function toggleLegend() { } function togglePercentageMode() { - if (body.dataset.type === "absolute") { - body.dataset.type = "percentage"; + if ($body.dataset.type === "absolute") { + $body.dataset.type = "percentage"; const totalCells = +statesFooterCells.innerHTML; const totalBurgs = +statesFooterBurgs.innerHTML; const totalArea = +statesFooterArea.dataset.area; const totalPopulation = +statesFooterPopulation.dataset.population; - body.querySelectorAll(":scope > div").forEach(function (el) { + $body.querySelectorAll(":scope > div").forEach(function (el) { el.querySelector(".stateCells").innerHTML = rn((+el.dataset.cells / totalCells) * 100) + "%"; el.querySelector(".stateBurgs").innerHTML = rn((+el.dataset.burgs / totalBurgs) * 100) + "%"; el.querySelector(".stateArea").innerHTML = rn((+el.dataset.area / totalArea) * 100) + "%"; el.querySelector(".statePopulation").innerHTML = rn((+el.dataset.population / totalPopulation) * 100) + "%"; }); } else { - body.dataset.type = "absolute"; + $body.dataset.type = "absolute"; statesEditorAddLines(); } } @@ -741,7 +737,7 @@ function showStatesChart() { .attr("text-anchor", "middle") .attr("dominant-baseline", "central"); const graph = svg.append("g").attr("transform", `translate(-50, 0)`); - document.getElementById("statesTreeType").addEventListener("change", updateChart); + byId("statesTreeType").on("change", updateChart); treeLayout(root); @@ -799,7 +795,7 @@ function showStatesChart() { function hideInfo(ev) { stateHighlightOff(ev); - if (!document.getElementById("statesInfo")) return; + if (!byId("statesInfo")) return; statesInfo.innerHTML = "‍"; d3.select(ev.target).select("circle").classed("selected", 0); } @@ -847,10 +843,14 @@ function showStatesChart() { } function openRegenerationMenu() { - statesBottom.querySelectorAll(":scope > button").forEach(el => (el.style.display = "none")); - statesRegenerateButtons.style.display = "block"; + byId("statesBottom") + .querySelectorAll(":scope > button") + .forEach(el => (el.style.display = "none")); + byId("statesRegenerateButtons").style.display = "block"; - statesEditor.querySelectorAll(".show").forEach(el => el.classList.remove("hidden")); + byId("statesEditor") + .querySelectorAll(".show") + .forEach(el => el.classList.remove("hidden")); $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); } @@ -870,8 +870,8 @@ function recalculateStates(must) { function changeStatesGrowthRate() { const growthRate = +this.value; - document.getElementById("statesNeutral").value = growthRate; - document.getElementById("statesNeutralNumber").value = growthRate; + byId("statesNeutral").value = growthRate; + byId("statesNeutralNumber").value = growthRate; statesNeutral = growthRate; tip("Growth rate: " + growthRate); recalculateStates(false); @@ -882,15 +882,19 @@ function randomizeStatesExpansion() { if (!s.i || s.removed) return; const expansionism = rn(Math.random() * 4 + 1, 1); s.expansionism = expansionism; - body.querySelector("div.states[data-id='" + s.i + "'] > input.statePower").value = expansionism; + $body.querySelector("div.states[data-id='" + s.i + "'] > input.statePower").value = expansionism; }); recalculateStates(true, true); } function exitRegenerationMenu() { - statesBottom.querySelectorAll(":scope > button").forEach(el => (el.style.display = "inline-block")); - statesRegenerateButtons.style.display = "none"; - statesEditor.querySelectorAll(".show").forEach(el => el.classList.add("hidden")); + byId("statesBottom") + .querySelectorAll(":scope > button") + .forEach(el => (el.style.display = "inline-block")); + byId("statesRegenerateButtons").style.display = "none"; + byId("statesEditor") + .querySelectorAll(".show") + .forEach(el => el.classList.add("hidden")); $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); } @@ -899,24 +903,26 @@ function enterStatesManualAssignent() { customization = 2; statesBody.append("g").attr("id", "temp"); document.querySelectorAll("#statesBottom > button").forEach(el => (el.style.display = "none")); - document.getElementById("statesManuallyButtons").style.display = "inline-block"; - document.getElementById("statesHalo").style.display = "none"; + byId("statesManuallyButtons").style.display = "inline-block"; + byId("statesHalo").style.display = "none"; - statesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden")); + byId("statesEditor") + .querySelectorAll(".hide") + .forEach(el => el.classList.add("hidden")); statesFooter.style.display = "none"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); tip("Click on state to select, drag the circle to change state", true); viewbox.style("cursor", "crosshair").on("click", selectStateOnMapClick).call(d3.drag().on("start", dragStateBrush)).on("touchmove mousemove", moveStateBrush); - body.querySelector("div").classList.add("selected"); + $body.querySelector("div").classList.add("selected"); } function selectStateOnLineClick() { if (customization !== 2) return; if (this.parentNode.id !== "statesBodySection") return; - body.querySelector("div.selected").classList.remove("selected"); + $body.querySelector("div.selected").classList.remove("selected"); this.classList.add("selected"); } @@ -928,8 +934,8 @@ function selectStateOnMapClick() { const assigned = statesBody.select("#temp").select("polygon[data-cell='" + i + "']"); const state = assigned.size() ? +assigned.attr("data-state") : pack.cells.state[i]; - body.querySelector("div.selected").classList.remove("selected"); - body.querySelector("div[data-id='" + state + "']").classList.add("selected"); + $body.querySelector("div.selected").classList.remove("selected"); + $body.querySelector("div[data-id='" + state + "']").classList.add("selected"); } function dragStateBrush() { @@ -949,9 +955,9 @@ function dragStateBrush() { // change state within selection function changeStateForSelection(selection) { const temp = statesBody.select("#temp"); - const selected = body.querySelector("div.selected"); - const stateNew = +selected.dataset.id; + const $selected = $body.querySelector("div.selected"); + const stateNew = +$selected.dataset.id; const color = pack.states[stateNew].color || "#ffffff"; selection.forEach(function (i) { @@ -999,7 +1005,7 @@ function applyStatesManualAssignent() { if (layerIsOn("toggleProvinces")) drawProvinces(); } - exitStatesManualAssignment(); + exitStatesManualAssignment(false); } function adjustProvinces(affectedProvinces) { @@ -1139,17 +1145,19 @@ function exitStatesManualAssignment(close) { statesBody.select("#temp").remove(); removeCircle(); document.querySelectorAll("#statesBottom > button").forEach(el => (el.style.display = "inline-block")); - document.getElementById("statesManuallyButtons").style.display = "none"; - document.getElementById("statesHalo").style.display = "block"; + byId("statesManuallyButtons").style.display = "none"; + byId("statesHalo").style.display = "block"; - statesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden")); + byId("statesEditor") + .querySelectorAll(".hide:not(.show)") + .forEach(el => el.classList.remove("hidden")); statesFooter.style.display = "block"; - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); if (!close) $("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}}); restoreDefaultEvents(); clearMainTip(); - const selected = body.querySelector("div.selected"); + const selected = $body.querySelector("div.selected"); if (selected) selected.classList.remove("selected"); } @@ -1162,7 +1170,7 @@ function enterAddStateMode() { this.classList.add("pressed"); tip("Click on the map to create a new capital or promote an existing burg", true); viewbox.style("cursor", "crosshair").on("click", addState); - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "none")); } function addState() { @@ -1276,41 +1284,30 @@ function exitAddStateMode() { customization = 0; restoreDefaultEvents(); clearMainTip(); - body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); + $body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all")); if (statesAdd.classList.contains("pressed")) statesAdd.classList.remove("pressed"); } -function downloadStatesData() { +function downloadStatesCsv() { const unit = getAreaUnit("2"); - let data = - "Id,State,Full Name,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers - body.querySelectorAll(":scope > div").forEach(function (el) { - const key = parseInt(el.dataset.id); - const statePack = pack.states[key]; - data += el.dataset.id + ","; - data += el.dataset.name + ","; - data += (statePack.fullName ? statePack.fullName : "") + ","; - data += el.dataset.form + ","; - data += el.dataset.color + ","; - data += el.dataset.capital + ","; - data += el.dataset.culture + ","; - data += el.dataset.type + ","; - data += el.dataset.expansionism + ","; - data += el.dataset.cells + ","; - data += el.dataset.burgs + ","; - data += el.dataset.area + ","; - data += el.dataset.population + ","; - data += `${Math.round(statePack.rural * populationRate)},`; - data += `${Math.round(statePack.urban * populationRate * urbanization)}\n`; + const headers = `Id,State,Full Name,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area ${unit},Total Population,Rural Population,Urban Population`; + const lines = Array.from($body.querySelectorAll(":scope > div")); + const data = lines.map($line => { + const {id, name, form, color, capital, culture, type, expansionism, cells, burgs, area, population} = $line.dataset; + const {fullName = "", rural, urban} = pack.states[+id]; + const ruralPopulation = Math.round(rural * populationRate); + const urbanPopulation = Math.round(urban * populationRate * urbanization); + return [id, name, fullName, form, color, capital, culture, type, expansionism, cells, burgs, area, population, ruralPopulation, urbanPopulation].join(","); }); + const csvData = [headers].concat(data).join("\n"); const name = getFileName("States") + ".csv"; - downloadFile(data, name); + downloadFile(csvData, name); } function closeStatesEditor() { - if (customization === 2) exitStatesManualAssignment("close"); + if (customization === 2) exitStatesManualAssignment(true); if (customization === 3) exitAddStateMode(); debug.selectAll(".highlight").remove(); - body.innerHTML = ""; + $body.innerHTML = ""; } diff --git a/modules/ui/editors.js b/modules/ui/editors.js index f8172db5..494a6270 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -1119,12 +1119,12 @@ function refreshAllEditors() { // dynamically loaded editors async function editStates() { if (customization) return; - const StateEditor = await import("../dynamic/editors/states-editor.js?v=18052022"); + const StateEditor = await import("../dynamic/editors/states-editor.js?v=20052022"); StateEditor.open(); } async function editCultures() { if (customization) return; - const CulturesEditor = await import("../dynamic/editors/cultures-editor.js"); + const CulturesEditor = await import("../dynamic/editors/cultures-editor.js?v=20052022"); CulturesEditor.open(); } diff --git a/utils/shorthands.js b/utils/shorthands.js new file mode 100644 index 00000000..043599fc --- /dev/null +++ b/utils/shorthands.js @@ -0,0 +1,9 @@ +const byId = document.getElementById.bind(document); + +Node.prototype.on = function (name, fn, options) { + this.addEventListener(name, fn, options); +}; + +Node.prototype.off = function (name, fn) { + this.removeEventListener(name, fn); +}; diff --git a/versioning.js b/versioning.js index 767559d4..ee337422 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // version and caching control -const version = "1.82.04"; // generator version, update each time +const version = "1.82.05"; // generator version, update each time { document.title += " v" + version;