refactor: map events

This commit is contained in:
Azgaar 2022-07-10 02:35:30 +03:00
parent 3c850d8d46
commit ff97c9227d
32 changed files with 433 additions and 329 deletions

View file

@ -1,7 +1,7 @@
import * as d3 from "d3";
import {closeDialogs} from "dialogs/utils";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, showMainTip, tip} from "scripts/tooltips";
import {getRandomColor} from "utils/colorUtils";
import {findAll, findCell, getPackPolygon, isLand} from "utils/graphUtils";
@ -467,7 +467,7 @@ export function open() {
biomesFooter.style.display = "block";
if (!close) $("#biomesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const selected = document.querySelector("#biomesBody > div.selected");
if (selected) selected.classList.remove("selected");

View file

@ -11,7 +11,7 @@ import {rn} from "utils/numberUtils";
import {rand} from "utils/probabilityUtils";
import {parseTransform} from "utils/stringUtils";
import {convertTemperature, getHeight, getBurgPopulation, getBurgPopulationPoints} from "utils/unitUtils";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
let isLoaded = false;
@ -484,7 +484,7 @@ export function open({id} = {}) {
}
} else {
clearMainTip();
restoreDefaultEvents();
setDefaultEventHandlers();
if (layerIsOn("toggleCells") && toggler.dataset.forced) {
toggleCells();

View file

@ -4,7 +4,7 @@ import {openDialog} from "dialogs";
import {closeDialogs} from "dialogs/utils";
import {layerIsOn, toggleLayer} from "layers";
import {applySorting} from "modules/ui/editors";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, tip} from "scripts/tooltips";
import {getCoordinates} from "utils/coordinateUtils";
import {findCell} from "utils/graphUtils";
@ -312,7 +312,7 @@ export function open() {
function exitAddBurgMode() {
customization = 0;
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
if (addBurgTool.classList.contains("pressed")) addBurgTool.classList.remove("pressed");
if (addNewBurg.classList.contains("pressed")) addNewBurg.classList.remove("pressed");

View file

@ -2,7 +2,7 @@ import * as d3 from "d3";
import {openDialog} from "dialogs";
import {closeDialogs} from "dialogs/utils";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, showMainTip, tip} from "scripts/tooltips";
import {debounce} from "utils/functionUtils";
import {findAll, findCell, getPackPolygon, isLand} from "utils/graphUtils";
@ -811,7 +811,7 @@ function exitCulturesManualAssignment(close) {
if (!close) $("#culturesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
debug.select("#cultureCenters").style("display", null);
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const selected = $body.querySelector("div.selected");
if (selected) selected.classList.remove("selected");
@ -829,7 +829,7 @@ function enterAddCulturesMode() {
function exitAddCultureMode() {
customization = 0;
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
$body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all"));
if (culturesAdd.classList.contains("pressed")) culturesAdd.classList.remove("pressed");

View file

@ -2,7 +2,7 @@ import * as d3 from "d3";
import {closeDialogs} from "dialogs/utils";
import {layerIsOn} from "layers";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, tip} from "scripts/tooltips";
import {findCell} from "utils/graphUtils";
import {applySorting} from "modules/ui/editors";
@ -455,7 +455,7 @@ export function open() {
}
function closeDiplomacyEditor() {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const selected = body.querySelector("div.Self");
if (selected) selected.classList.remove("Self");

View file

@ -13,7 +13,7 @@ import {moveCircle, removeCircle} from "modules/ui/editors";
import {changeViewMode} from "modules/ui/options";
import {addZones} from "modules/zones";
import {aleaPRNG} from "scripts/aleaPRNG";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {undraw} from "scripts/generation";
import {prompt} from "scripts/prompt";
import {rankCells} from "scripts/rankCells";
@ -182,7 +182,7 @@ export function open(options) {
if (byId("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block";
layersPreset.disabled = false;
exitCustomization.style.display = "none"; // hide finalize button
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
closeDialogs();
Zoom.reset();

View file

@ -11,8 +11,7 @@ import {byId} from "utils/shorthands";
import {parseTransform} from "utils/stringUtils";
// @ts-expect-error js module
import {editStyle} from "modules/style";
// @ts-expect-error js module
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
// @ts-expect-error js module
import {unselect} from "modules/ui/editors";
@ -85,7 +84,7 @@ export function open() {
tip("Click on map to create an iceberg. Hold Shift to add multiple", true);
} else {
clearMainTip();
restoreDefaultEvents();
setDefaultEventHandlers();
}
}

View file

@ -3,7 +3,7 @@ import * as d3 from "d3";
import {openDialog} from "dialogs";
import {closeDialogs} from "dialogs/utils";
import {applySortingByHeader} from "modules/ui/editors";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, showMainTip, tip} from "scripts/tooltips";
import {debounce} from "utils/functionUtils";
import {findAll, findCell, getPackPolygon, isLand} from "utils/graphUtils";
@ -710,7 +710,7 @@ function exitReligionsManualAssignment(close) {
if (!close) $("#religionsEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
debug.select("#religionCenters").style("display", null);
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const $selected = $body.querySelector("div.selected");
if ($selected) $selected.classList.remove("selected");
@ -728,7 +728,7 @@ function enterAddReligionMode() {
function exitAddReligionMode() {
customization = 0;
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
$body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all"));
if (religionsAdd.classList.contains("pressed")) religionsAdd.classList.remove("pressed");

View file

@ -3,7 +3,7 @@ import * as d3 from "d3";
import {openDialog} from "dialogs";
import {closeDialogs} from "dialogs/utils";
import {applySortingByHeader, unfog} from "modules/ui/editors";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, showMainTip, tip} from "scripts/tooltips";
import {getMixedColor, getRandomColor} from "utils/colorUtils";
import {findAll, findCell, getPackPolygon, isLand} from "utils/graphUtils";
@ -1217,7 +1217,7 @@ function exitStatesManualAssignment(close) {
if (!close)
$("#statesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const selected = $body.querySelector("div.selected");
if (selected) selected.classList.remove("selected");
@ -1349,7 +1349,7 @@ function addState() {
function exitAddStateMode() {
customization = 0;
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
$body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all"));
if (statesAdd.classList.contains("pressed")) statesAdd.classList.remove("pressed");

View file

@ -1,6 +1,6 @@
import * as d3 from "d3";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {findCell} from "utils/graphUtils";
import {tip} from "scripts/tooltips";
import {prompt} from "scripts/prompt";
@ -174,7 +174,7 @@ export function open() {
function toggleOpisometerMode() {
if (this.classList.contains("pressed")) {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
this.classList.remove("pressed");
} else {
@ -193,7 +193,7 @@ export function open() {
});
d3.event.on("end", function () {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
addOpisometer.classList.remove("pressed");
if (opisometer.points.length < 2) rulers.remove(opisometer.id);
@ -206,7 +206,7 @@ export function open() {
function toggleRouteOpisometerMode() {
if (this.classList.contains("pressed")) {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
this.classList.remove("pressed");
} else {
@ -235,7 +235,7 @@ export function open() {
});
d3.event.on("end", function () {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
addRouteOpisometer.classList.remove("pressed");
if (routeOpisometer.points.length < 2) {
@ -243,7 +243,7 @@ export function open() {
}
});
} else {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
addRouteOpisometer.classList.remove("pressed");
tip("Must start in a cell with a route in it", false, "error");
@ -255,7 +255,7 @@ export function open() {
function togglePlanimeterMode() {
if (this.classList.contains("pressed")) {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
this.classList.remove("pressed");
} else {
@ -274,7 +274,7 @@ export function open() {
});
d3.event.on("end", function () {
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
addPlanimeter.classList.remove("pressed");
if (planimeter.points.length < 3) rulers.remove(planimeter.id);

View file

@ -2,7 +2,7 @@ import * as d3 from "d3";
import {INFO} from "config/logging";
import {updatePresetInput} from "layers";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {ldb} from "scripts/indexedDB";
import {tip} from "scripts/tooltips";
import {last} from "utils/arrayUtils";
@ -624,7 +624,7 @@ async function parseLoadedData(data) {
eraInput.value = options.era;
shapeRendering.value = viewbox.attr("shape-rendering") || "geometricPrecision";
restoreDefaultEvents();
setDefaultEventHandlers();
focusOn(); // based on searchParams focus on point, cell or burg
Zoom.invoke();

View file

@ -1,6 +1,6 @@
import * as d3 from "d3";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {tip} from "scripts/tooltips";
import {findCell} from "utils/graphUtils";
import {minmax, normalize, rn} from "utils/numberUtils";
@ -11,7 +11,7 @@ import {getBurgPopulation} from "utils/unitUtils";
// clear elSelected variable
export function unselect() {
restoreDefaultEvents();
setDefaultEventHandlers();
if (!elSelected) return;
elSelected.call(d3.drag().on("drag", null)).attr("class", null);
debug.selectAll("*").remove();

View file

@ -1,6 +1,6 @@
import * as d3 from "d3";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {findCell} from "utils/graphUtils";
import {clearMainTip} from "scripts/tooltips";
import {rn} from "utils/numberUtils";
@ -271,7 +271,7 @@ export function editMarker(markerI) {
unselect();
addMarker.classList.remove("pressed");
markerAdd.classList.remove("pressed");
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
}
}

View file

@ -1,4 +1,4 @@
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip} from "scripts/tooltips";
import {closeDialogs} from "dialogs/utils";
@ -197,7 +197,7 @@ export function overviewMarkers() {
addMarker.classList.remove("pressed");
markerAdd.classList.remove("pressed");
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
}
}

View file

@ -4,7 +4,7 @@ import {openDialog} from "dialogs";
import {closeDialogs} from "dialogs/utils";
import {turnLayerButtonOff} from "layers";
import {unfog} from "modules/ui/editors";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, showMainTip, tip} from "scripts/tooltips";
import {unique} from "utils/arrayUtils";
import {getRandomColor} from "utils/colorUtils";
@ -993,7 +993,7 @@ export function editProvinces() {
if (!close)
$("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const selected = body.querySelector("div.selected");
if (selected) selected.classList.remove("selected");
@ -1069,7 +1069,7 @@ export function editProvinces() {
function exitAddProvinceMode() {
customization = 0;
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all"));
if (provincesAdd.classList.contains("pressed")) provincesAdd.classList.remove("pressed");

View file

@ -1,6 +1,6 @@
import * as d3 from "d3";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {findCell} from "utils/graphUtils";
import {last} from "utils/arrayUtils";
import {tip, clearMainTip} from "scripts/tooltips";
@ -210,7 +210,7 @@ export function editRegiment(selector) {
tip("Click on map to create new regiment or fleet", true);
} else {
clearMainTip();
restoreDefaultEvents();
setDefaultEventHandlers();
}
}
@ -240,7 +240,7 @@ export function editRegiment(selector) {
armies.selectAll(":scope > g").classed("draggable", false);
} else {
clearMainTip();
restoreDefaultEvents();
setDefaultEventHandlers();
armies.selectAll(":scope > g").classed("draggable", true);
}
}
@ -313,7 +313,7 @@ export function editRegiment(selector) {
} else {
clearMainTip();
armies.selectAll(":scope > g").classed("draggable", true);
restoreDefaultEvents();
setDefaultEventHandlers();
}
}
@ -453,7 +453,7 @@ export function editRegiment(selector) {
document.getElementById("regimentAdd").classList.remove("pressed");
document.getElementById("regimentAttack").classList.remove("pressed");
document.getElementById("regimentAttach").classList.remove("pressed");
restoreDefaultEvents();
setDefaultEventHandlers();
elSelected = null;
}
}

View file

@ -7,7 +7,7 @@ import {rn} from "utils/numberUtils";
import {capitalize} from "utils/stringUtils";
import {si} from "utils/unitUtils";
import {closeDialogs} from "dialogs/utils";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
let isLoaded = false;
@ -171,7 +171,7 @@ export function overviewRegiments(state) {
if (regimentAdd.offsetParent) regimentAdd.classList.add("pressed");
} else {
clearMainTip();
restoreDefaultEvents();
setDefaultEventHandlers();
addLines();
if (regimentAdd.offsetParent) regimentAdd.classList.remove("pressed");
}

View file

@ -1,6 +1,6 @@
import * as d3 from "d3";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {findCell} from "utils/graphUtils";
import {tip, showMainTip, clearMainTip} from "scripts/tooltips";
import {rn} from "utils/numberUtils";
@ -92,7 +92,7 @@ export function editReliefIcon() {
removeCircle();
updateReliefSizeInput();
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
}

View file

@ -1,6 +1,6 @@
import * as d3 from "d3";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {getPackPolygon, findCell} from "utils/graphUtils";
import {last} from "utils/arrayUtils";
import {tip, clearMainTip} from "scripts/tooltips";
@ -140,7 +140,7 @@ export function createRiver() {
function closeRiverCreator() {
body.innerHTML = "";
debug.select("#controlCells").remove();
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
const forced = +document.getElementById("toggleCells").dataset.forced;

View file

@ -6,7 +6,7 @@ import {rn} from "utils/numberUtils";
import {getNextId} from "utils/nodeUtils";
import {round} from "utils/stringUtils";
import {closeDialogs} from "dialogs/utils";
import {restoreDefaultEvents} from "../../scripts/events";
import {setDefaultEventHandlers} from "../../scripts/events";
let isLoaded = false;
@ -279,7 +279,7 @@ export function editRoute(onClick) {
elSelected.on("click", null);
} else {
clearMainTip();
restoreDefaultEvents();
setDefaultEventHandlers();
elSelected.on("click", addInterimControlPoint).attr("data-new", null);
}
}

View file

@ -5,7 +5,7 @@ import {closeDialogs} from "dialogs/utils";
import {renderLayer, toggleLayer, turnLayerButtonOn} from "layers";
import {unfog} from "modules/ui/editors";
import {aleaPRNG} from "scripts/aleaPRNG";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {prompt} from "scripts/prompt";
import {clearMainTip, tip} from "scripts/tooltips";
import {last} from "utils/arrayUtils";
@ -465,7 +465,7 @@ function regenerateZones(event) {
function unpressClickToAddButton() {
addFeature.querySelectorAll("button.pressed").forEach(b => b.classList.remove("pressed"));
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
}

View file

@ -2,7 +2,7 @@ import * as d3 from "d3";
import {closeDialogs} from "dialogs/utils";
import {unfog} from "modules/ui/editors";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {clearMainTip, showMainTip, tip} from "scripts/tooltips";
import {unique} from "utils/arrayUtils";
import {findAll, findCell, getPackPolygon} from "utils/graphUtils";
@ -329,7 +329,7 @@ export function editZones() {
if (!close)
$("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
restoreDefaultEvents();
setDefaultEventHandlers();
clearMainTip();
zones.selectAll("g").each(function () {
this.removeAttribute("data-init");

View file

@ -1,262 +0,0 @@
import * as d3 from "d3";
import {openDialog} from "dialogs";
import {layerIsOn} from "layers";
// @ts-expect-error js module
import {clearLegend, dragLegendBox} from "modules/legend";
// @ts-expect-error js module
import {updateCellInfo} from "modules/ui/cell-info";
import {debounce} from "utils/functionUtils";
import {findCell, findGridCell, isLand} from "utils/graphUtils";
import {byId} from "utils/shorthands";
import {
convertTemperature,
getBurgPopulation,
getCellIdPrecipitation,
getFriendlyHeight,
getCellPopulation,
getPopulationTip,
si
} from "utils/unitUtils";
import {showMainTip, tip} from "./tooltips";
export function restoreDefaultEvents() {
window.Zoom.setZoomBehavior();
viewbox
.style("cursor", "default")
.on(".drag", null)
.on("click", handleMapClick)
.on("touchmove mousemove", onMouseMove);
scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => openDialog("unitsEditor"));
legend
.on("mousemove", () => tip("Drag to change the position. Click to hide the legend"))
.on("click", clearLegend)
.call(d3.drag().on("start", dragLegendBox));
}
// on viewbox click event - run function based on target
function handleMapClick(this: d3.ContainerElement) {
const el: HTMLElement = d3.event.target;
if (!el?.parentElement?.parentElement?.parentElement) return;
const parent = el.parentElement;
const grand = parent.parentElement!;
const great = grand.parentElement!;
const greatGreat = great.parentElement;
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
if (grand.id === "emblems" && defineEmblemData(el)) openDialog("emblemEditor", null, defineEmblemData(el));
else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && greatGreat?.id === "labels") openDialog("labelEditor", null, {el});
else if (grand.id === "burgLabels" || grand.id === "burgIcons")
openDialog("burgEditor", null, {id: +(el.dataset.id || 0)});
else if (parent.id === "ice") openDialog("iceEditor");
else if (parent.id === "terrain") editReliefIcon();
else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") openDialog("coastlineEditor", null, {el});
else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) {
openDialog("coastlineEditor", null, {node: byId("island_" + pack.cells.f[i])});
} else if (grand.id === "lakes") openDialog("lakeEditor", null, {el});
}
function defineEmblemData(el: HTMLElement) {
const i = +(el.dataset?.i || 0);
type TEmblemType = "state" | "burg" | "province";
type TEmblemTypeArray = IPack[`${TEmblemType}s`];
const emblemTypeMap: Dict<[TEmblemTypeArray, TEmblemType]> = {
burgEmblems: [pack.burgs, "burg"],
provinceEmblems: [pack.provinces, "province"],
stateEmblems: [pack.states, "state"]
};
const emblemType = el.parentElement?.id;
if (emblemType && emblemType in emblemTypeMap) {
const [data, type] = emblemTypeMap[emblemType];
return {type, id: type + "COA" + i, el: data[i]};
}
return undefined;
}
const onMouseMove = debounce(handleMouseMove, 100);
function handleMouseMove(this: d3.ContainerElement) {
const point = d3.mouse(this);
const i = findCell(point[0], point[1]); // pack cell id
if (i === undefined) return;
showNotes(d3.event);
const gridCell = findGridCell(point[0], point[1], grid);
if (byId("tooltip")?.dataset.main) showMainTip();
else showTooltipOnMapHover(point, d3.event, i, gridCell);
if (byId("cellInfo")?.offsetParent) updateCellInfo(point, i, gridCell);
}
// show note box on hover (if any)
function showNotes(event: Event) {
if (byId("notesEditor")?.offsetParent) return;
const el = event.target as SVGElement;
if (!el?.parentElement?.parentElement?.parentElement) return;
const parent = el.parentElement;
const grand = parent.parentElement!;
let id = el.id || parent.id || grand.id;
if (grand.id === "burgLabels") id = "burg" + el.dataset.id;
else if (grand.id === "burgIcons") id = "burg" + el.dataset.id;
const note = notes.find(note => note.id === id);
if (note !== undefined && note.legend !== "") {
byId("notes")!.style.display = "block";
byId("notesHeader")!.innerHTML = note.name;
byId("notesBody")!.innerHTML = note.legend;
} else if (!options.pinNotes && !byId("markerEditor")?.offsetParent) {
byId("notes")!.style.display = "none";
byId("notesHeader")!.innerHTML = "";
byId("notesBody")!.innerHTML = "";
}
}
// show viewbox tooltip if main tooltip is blank
function showTooltipOnMapHover(point: TPoint, event: Event, packCellId: number, gridCellId: number) {
tip(""); // clear tip
const path = event.composedPath() as HTMLElement[];
if (!path[path.length - 8]) return;
const group = path[path.length - 7].id;
const subgroup = path[path.length - 8].id;
const element = event.target as HTMLElement;
const parent = element.parentElement!;
const land = isLand(packCellId);
// specific elements
if (group === "armies") return tip(parent.dataset.name + ". Click to edit");
if (group === "emblems" && element.tagName === "use") {
d3.select(element).raise();
d3.select(parent).raise();
const emblemData = defineEmblemData(element);
if (!emblemData) return;
const {type, el} = emblemData;
const name = ("fullName" in el && el.fullName) || el.name;
tip(`${name} ${type} emblem. Click to edit`);
return;
}
if (group === "rivers") {
const riverId = +element.id.slice(5);
const river = pack.rivers.find(r => r.i === riverId);
const name = river ? `${river.name} ${river.type}` : "";
tip(name + ". Click to edit");
const $riversOverview = byId("riversOverview")!;
if ($riversOverview?.offsetParent) highlightEditorLine($riversOverview, riverId, 5000);
return;
}
if (group === "routes") return tip("Click to edit the Route");
if (group === "terrain") return tip("Click to edit the Relief Icon");
if (subgroup === "burgLabels" || subgroup === "burgIcons") {
const burgId = +(path[path.length - 10].dataset.id || 0);
const burg = pack.burgs[burgId];
const population = si(getBurgPopulation(burg.population));
tip(`${burg.name}. Population: ${population}. Click to edit`);
const $burgOverview = byId("burgOverview");
if ($burgOverview?.offsetParent) highlightEditorLine($burgOverview, burgId, 5000);
return;
}
if (group === "labels") return tip("Click to edit the Label");
if (group === "markers") return tip("Click to edit the Marker and pin the marker note");
if (group === "ruler") {
const tag = element.tagName;
const className = element.getAttribute("class");
if (tag === "circle" && className === "edge")
return tip("Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point");
if (tag === "circle" && className === "control")
return tip("Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point");
if (tag === "circle") return tip("Drag to adjust the measurer");
if (tag === "polyline") return tip("Click on drag to add a control point");
if (tag === "path") return tip("Drag to move the measurer");
if (tag === "text") return tip("Drag to move, click to remove the measurer");
}
if (subgroup === "burgIcons") return tip("Click to edit the Burg");
if (subgroup === "burgLabels") return tip("Click to edit the Burg");
if (group === "lakes" && !land) {
const lakeId = +element.dataset.f;
const name = pack.features[lakeId]?.name;
const fullName = subgroup === "freshwater" ? name : name + " " + subgroup;
tip(`${fullName} lake. Click to edit`);
return;
}
if (group === "coastline") return tip("Click to edit the coastline");
if (group === "zones") {
const zone = path[path.length - 8];
tip(zone.dataset.description);
if (zonesEditor?.offsetParent) highlightEditorLine(zonesEditor, zone.id, 5000);
return;
}
if (group === "ice") return tip("Click to edit the Ice");
// covering elements
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: " + getCellIdPrecipitation(packCellId));
else if (layerIsOn("togglePopulation")) {
const [rural, urban] = getCellPopulation(packCellId);
tip(getPopulationTip("Cell population", rural, urban));
} else if (layerIsOn("toggleTemp")) tip("Temperature: " + convertTemperature(grid.cells.temp[gridCellId]));
else if (layerIsOn("toggleBiomes") && pack.cells.biome[packCellId]) {
const biome = pack.cells.biome[packCellId];
tip("Biome: " + biomesData.name[biome]);
if (biomesEditor?.offsetParent) highlightEditorLine(biomesEditor, biome);
} else if (layerIsOn("toggleReligions") && pack.cells.religion[packCellId]) {
const religion = pack.cells.religion[packCellId];
const r = pack.religions[religion];
const type = r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion";
tip(type + ": " + r.name);
if (religionsEditor?.offsetParent) highlightEditorLine(religionsEditor, religion);
} else if (pack.cells.state[packCellId] && (layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))) {
const state = pack.cells.state[packCellId];
const stateName = pack.states[state].fullName;
const province = pack.cells.province[packCellId];
const prov = province ? pack.provinces[province].fullName + ", " : "";
tip(prov + stateName);
if (byId("statesEditor")?.offsetParent) highlightEditorLine(statesEditor, state);
if (byId("diplomacyEditor")?.offsetParent) highlightEditorLine(diplomacyEditor, state);
if (byId("militaryOverview")?.offsetParent) highlightEditorLine(militaryOverview, state);
if (byId("provincesEditor")?.offsetParent) highlightEditorLine(provincesEditor, province);
} else if (layerIsOn("toggleCultures") && pack.cells.culture[packCellId]) {
const culture = pack.cells.culture[packCellId];
tip("Culture: " + pack.cultures[culture].name);
if (byId("culturesEditor")?.offsetParent) highlightEditorLine(culturesEditor, culture);
} else if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point));
}
function highlightEditorLine($editor, id, timeout = 10000) {
Array.from($editor.getElementsByClassName("states hovered")).forEach(el => el.classList.remove("hovered")); // clear all hovered
const hovered = Array.from($editor.querySelectorAll("div")).find(el => el.dataset.id == id);
if (hovered) hovered.classList.add("hovered"); // add hovered class
if (timeout)
setTimeout(() => {
hovered && hovered.classList.remove("hovered");
}, timeout);
}

View file

@ -0,0 +1,25 @@
import * as d3 from "d3";
import {openDialog} from "dialogs";
import {tip} from "scripts/tooltips";
import {handleMapClick} from "./onclick";
import {onMouseMove} from "./onhover";
// @ts-expect-error js module
import {clearLegend, dragLegendBox} from "modules/legend";
export function setDefaultEventHandlers() {
window.Zoom.setZoomBehavior();
viewbox
.style("cursor", "default")
.on(".drag", null)
.on("click", handleMapClick)
.on("touchmove mousemove", onMouseMove);
scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", () => openDialog("unitsEditor"));
legend
.on("mousemove", () => tip("Drag to change the position. Click to hide the legend"))
.on("click", clearLegend)
.call(d3.drag().on("start", dragLegendBox));
}

View file

@ -0,0 +1,65 @@
import * as d3 from "d3";
import {openDialog} from "dialogs";
// @ts-expect-error js module
import {clearLegend, dragLegendBox} from "modules/legend";
// @ts-expect-error js module
import {updateCellInfo} from "modules/ui/cell-info";
import {findCell} from "utils/graphUtils";
import {defineEmblemData} from "./utils";
const getClickedElement = (
tagName: string,
parentId: string,
grandId: string,
greatId: string,
greatGrandId: string,
isCoastalCell: boolean
) => {
if (grandId === "emblems") return "emblem";
if (parentId === "rivers") return "river";
if (grandId === "routes") return "route";
if (tagName === "tspan" && greatGrandId === "labels") return "label";
if (grandId === "burgLabels" || grandId === "burgIcons") return "burg";
if (parentId === "ice") return "ice";
if (parentId === "terrain") return "reliefIcon";
if (grandId === "markers" || greatId === "markers") return "marker";
if (grandId === "coastline" || isCoastalCell) return "coastline";
if (greatId === "armies") return "regiment";
if (grandId === "lakes") return "lake";
return null;
};
type ClickedElement = ReturnType<typeof getClickedElement>;
type OnClickEvent = (el: HTMLElement) => void;
type OnClickEventMap = {[key in Exclude<ClickedElement, null>]: OnClickEvent};
const onClickEventsMap: OnClickEventMap = {
emblem: el => openDialog("emblemEditor", null, defineEmblemData(el)),
river: el => openDialog("riverEditor", null, el.id),
route: () => openDialog("routeEditor"),
label: el => openDialog("labelEditor", null, {el}),
burg: el => openDialog("burgEditor", null, {id: +(el.dataset.id || 0)}),
ice: () => openDialog("iceEditor"),
reliefIcon: () => openDialog("reliefEditor"),
marker: () => openDialog("markerEditor"),
coastline: el => openDialog("coastlineEditor", null, {el}),
regiment: () => openDialog("regimentEditor"),
lake: el => openDialog("lakeEditor", null, {el})
};
// on viewbox click event - run function based on target
export function handleMapClick(this: d3.ContainerElement) {
const path = d3.event.composedPath() as HTMLElement[];
const [el, parent, grand, great, greatGrand] = path;
if (!el || !parent || !grand || !great || !greatGrand) return;
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
const isCoastalCell = pack.cells.t[i] === 1;
const clickedElement = getClickedElement(el.tagName, parent.id, grand.id, great.id, greatGrand.id, isCoastalCell);
if (clickedElement && clickedElement in onClickEventsMap) {
onClickEventsMap[clickedElement](el);
}
}

View file

@ -0,0 +1,251 @@
import * as d3 from "d3";
import {layerIsOn} from "layers";
// @ts-expect-error js module
import {clearLegend, dragLegendBox} from "modules/legend";
// @ts-expect-error js module
import {updateCellInfo} from "modules/ui/cell-info";
import {debounce} from "utils/functionUtils";
import {findCell, findGridCell, isLand} from "utils/graphUtils";
import {byId} from "utils/shorthands";
import {
convertTemperature,
getBurgPopulation,
getCellIdPrecipitation,
getFriendlyHeight,
getCellPopulation,
getPopulationTip,
si
} from "utils/unitUtils";
import {showMainTip, tip} from "scripts/tooltips";
import {defineEmblemData} from "./utils";
export const onMouseMove = debounce(handleMouseMove, 100);
function handleMouseMove(this: d3.ContainerElement) {
const [x, y] = d3.mouse(this);
const i = findCell(x, y); // pack cell id
if (i === undefined) return;
showNotes(d3.event);
const gridCell = findGridCell(x, y, grid);
if (byId("tooltip")?.dataset.main) showMainTip();
else showTooltipOnMapHover([x, y], d3.event, i, gridCell);
if (byId("cellInfo")?.offsetParent) updateCellInfo([x, y], i, gridCell);
}
// show note box on hover (if any)
function showNotes(event: Event) {
if (byId("notesEditor")?.offsetParent) return;
const path = event.composedPath() as HTMLElement[];
const [el, parent, grand] = path;
if (!el || !parent || !grand) return;
let id = el.id || parent.id || grand.id;
if (grand.id === "burgLabels") id = "burg" + el.dataset.id;
else if (grand.id === "burgIcons") id = "burg" + el.dataset.id;
const note = notes.find(note => note.id === id);
if (note !== undefined && note.legend !== "") {
byId("notes")!.style.display = "block";
byId("notesHeader")!.innerHTML = note.name;
byId("notesBody")!.innerHTML = note.legend;
} else if (!options.pinNotes && !byId("markerEditor")?.offsetParent) {
byId("notes")!.style.display = "none";
byId("notesHeader")!.innerHTML = "";
byId("notesBody")!.innerHTML = "";
}
}
const getHoveredElement = (tagName: string, group: string, subgroup: string, isLand: boolean, cellId: number) => {
const {biome, religion, state, culture} = pack.cells;
if (group === "armies") return "regiment";
if (group === "emblems" && tagName === "use") return "emblem";
if (group === "rivers") return "river";
if (group === "routes") return "route";
if (group === "terrain") return "reliefIcon";
if (subgroup === "burgLabels" || subgroup === "burgIcons") return "burg";
if (group === "labels") return "label";
if (group === "markers") return "marker";
if (group === "ruler") return "ruler";
if (group === "lakes" && !isLand) return "lake";
if (group === "coastline") return "coastline";
if (group === "zones") return "zone";
if (group === "ice") return "ice";
if (layerIsOn("togglePrec") && isLand) return "precipitationLayer";
if (layerIsOn("togglePopulation")) return "populationLayer";
if (layerIsOn("toggleTemp")) return "temperatureLayer";
if (layerIsOn("toggleBiomes") && biome[cellId]) return "biomesLayer";
if (layerIsOn("toggleReligions") && religion[cellId]) return "religionsLayer";
if (layerIsOn("toggleProvinces") || (layerIsOn("toggleStates") && state[cellId])) return "statesLayer";
if (layerIsOn("toggleCultures") && culture[cellId]) return "culturesLayer";
if (layerIsOn("toggleHeight")) return "heightLayer";
return null;
};
type HoveredElement = ReturnType<typeof getHoveredElement>;
type OnHoverEvent = (props: {
path: HTMLElement[];
element: HTMLElement;
parent: HTMLElement;
subgroup: string;
point: TPoint;
packCellId: number;
gridCellId: number;
}) => void;
type OnHoverEventMap = {[key in Exclude<HoveredElement, null>]: OnHoverEvent};
const onHoverEventsMap: OnHoverEventMap = {
regiment: ({parent}) => tip(parent.dataset.name + ". Click to edit"),
emblem: ({element, parent}) => {
d3.select(element).raise();
d3.select(parent).raise();
const emblemData = defineEmblemData(element);
if (emblemData) {
const {type, el} = emblemData;
const name = ("fullName" in el && el.fullName) || el.name;
tip(`${name} ${type} emblem. Click to edit`);
}
},
river: ({element}) => {
const riverId = +element.id.slice(5);
const river = pack.rivers.find(r => r.i === riverId);
const name = river ? `${river.name} ${river.type}` : "";
tip(name + ". Click to edit");
highlightDialogLine("riversOverview", riverId, 5000);
},
route: () => tip("Click to edit the Route"),
reliefIcon: () => tip("Click to edit the Relief Icon"),
burg: ({path}) => {
const burgId = +(path.at(-10)?.dataset.id || 0);
const {population, name} = pack.burgs[burgId];
tip(`${name}. Population: ${si(getBurgPopulation(population))}. Click to edit`);
highlightDialogLine("burgOverview", burgId, 5000);
},
label: () => tip("Click to edit the Label"),
marker: () => tip("Click to edit the Marker and pin the marker note"),
ruler: ({element}) => {
const tag = element.tagName;
const className = element.getAttribute("class");
if (tag === "circle" && className === "edge")
return tip("Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point");
if (tag === "circle" && className === "control")
return tip("Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point");
if (tag === "circle") return tip("Drag to adjust the measurer");
if (tag === "polyline") return tip("Click on drag to add a control point");
if (tag === "path") return tip("Drag to move the measurer");
if (tag === "text") return tip("Drag to move, click to remove the measurer");
},
lake: ({element, subgroup}) => {
const lakeId = +(element.dataset.f || 0);
const name = pack.features[lakeId]?.name;
const fullName = subgroup === "freshwater" ? name : name + " " + subgroup;
tip(`${fullName} lake. Click to edit`);
},
coastline: () => tip("Click to edit the coastline"),
zone: ({path}) => {
const $zone = path[path.length - 8];
tip($zone.dataset.description || "");
highlightDialogLine("zonesEditor", $zone.id, 5000);
},
ice: () => tip("Click to edit the Ice"),
precipitationLayer: ({packCellId}) => tip("Annual Precipitation: " + getCellIdPrecipitation(packCellId)),
populationLayer: ({packCellId}) => {
const [rural, urban] = getCellPopulation(packCellId);
tip(getPopulationTip("Cell population", rural, urban));
},
temperatureLayer: ({gridCellId}) => tip("Temperature: " + convertTemperature(grid.cells.temp[gridCellId])),
biomesLayer: ({packCellId}) => {
const biome = pack.cells.biome[packCellId];
tip("Biome: " + biomesData.name[biome]);
highlightDialogLine("biomesEditor", biome);
},
religionsLayer: ({packCellId}) => {
const religionId = pack.cells.religion[packCellId];
const {type, name} = pack.religions[religionId] || {};
const typeTip = type === "Cult" || type == "Heresy" ? type : type + " religion";
tip(`${typeTip}: ${name}`);
highlightDialogLine("religionsEditor", religionId);
},
statesLayer: ({packCellId}) => {
const state = pack.cells.state[packCellId];
const stateName = pack.states[state].fullName;
const province = pack.cells.province[packCellId];
const prov = province ? `${pack.provinces[province].fullName}, ` : "";
tip(prov + stateName);
highlightDialogLine("statesEditor", state);
highlightDialogLine("diplomacyEditor", state);
highlightDialogLine("militaryEditor", state);
highlightDialogLine("provincesEditor", province);
},
culturesLayer: ({packCellId}) => {
const culture = pack.cells.culture[packCellId];
tip("Culture: " + pack.cultures[culture].name);
highlightDialogLine("culturesEditor", culture);
},
heightLayer: ({point}) => tip("Height: " + getFriendlyHeight(point))
};
// show viewbox tooltip if main tooltip is blank
function showTooltipOnMapHover(point: TPoint, event: Event, packCellId: number, gridCellId: number) {
tip(""); // clear tip
const path = event.composedPath() as HTMLElement[];
const [element, parent] = path;
if (!element || !parent || !path.at(-7) || !path.at(-8)) return;
const group = path.at(-7)!.id;
const subgroup = path.at(-8)!.id;
const land = isLand(packCellId);
const hoveredMapElement = getHoveredElement(element.tagName, group, subgroup, land, packCellId);
if (hoveredMapElement && hoveredMapElement in onHoverEventsMap) {
onHoverEventsMap[hoveredMapElement]({path, element, parent, subgroup, point, packCellId, gridCellId});
}
}
function highlightDialogLine(dialogId: string, lineId: number | string, timeout = 5000) {
const $dialog = byId(dialogId);
if (!$dialog || !$dialog.offsetParent) return; // check if dialog is visible
Array.from($dialog.getElementsByClassName("states hovered")).forEach(el => el.classList.remove("hovered")); // clear all hovered
const hovered = Array.from($dialog.querySelectorAll("div")).find(el => el.dataset.id === String(lineId));
if (hovered) hovered.classList.add("hovered"); // add hovered class
if (timeout)
setTimeout(() => {
hovered && hovered.classList.remove("hovered");
}, timeout);
}

View file

@ -0,0 +1,20 @@
type TEmblemType = "state" | "burg" | "province";
type TEmblemTypeArray = IPack[`${TEmblemType}s`];
const emblemTypeMap: {[key: string]: [TEmblemTypeArray, TEmblemType]} = {
burgEmblems: [pack.burgs, "burg"],
provinceEmblems: [pack.provinces, "province"],
stateEmblems: [pack.states, "state"]
};
export function defineEmblemData(el: HTMLElement) {
const i = +(el.dataset?.i || 0);
const emblemType = el.parentElement?.id;
if (emblemType && emblemType in emblemTypeMap) {
const [data, type] = emblemTypeMap[emblemType];
return {type, id: type + "COA" + i, el: data[i]};
}
return undefined;
}

View file

@ -2,7 +2,7 @@ import * as d3 from "d3";
import {ERROR, WARN} from "config/logging";
import {loadMapFromURL} from "modules/io/load";
import {restoreDefaultEvents} from "scripts/events";
import {setDefaultEventHandlers} from "scripts/events";
import {ldb} from "scripts/indexedDB";
import {getInputValue} from "utils/nodeUtils";
import {generateMapOnLoad} from "./generation";
@ -11,7 +11,7 @@ export function addOnLoadListener() {
document.on("DOMContentLoaded", async () => {
await loadOrGenerateMap();
hideLoading();
restoreDefaultEvents();
setDefaultEventHandlers();
});
}

1
src/types/grid.d.ts vendored
View file

@ -15,6 +15,7 @@ interface IGrid {
h: IntArray;
t: IntArray;
f: IntArray;
temp: IntArray;
prec: IntArray;
};
features: IFeature[];

11
src/types/pack.d.ts vendored
View file

@ -12,9 +12,16 @@ interface IPack {
c: number[][];
g: IntArray;
h: IntArray;
t: IntArray;
f: IntArray;
biome: IntArray;
pop: Float32Array;
burg: IntArray;
area: IntArray;
state: IntArray;
culture: IntArray;
religion: IntArray;
province: IntArray;
burg: IntArray;
q: d3.Quadtree<number[]>;
};
states: IState[];
@ -27,6 +34,7 @@ interface IPack {
interface IFeature {
i: number;
name: string;
}
interface IState {
@ -62,6 +70,7 @@ interface IBurg {
interface IReligion {
i: number;
name: string;
type: "Folk" | "Orgamized" | "Cult" | "Heresy";
removed?: boolean;
}

View file

@ -69,7 +69,7 @@ export function convertTemperature(temp: number) {
// ***
// get user-friendly (real-world) height value from coordinates
export function getFriendlyHeight([x, y]: [number, number]) {
export function getFriendlyHeight([x, y]: TPoint) {
const packH = pack.cells.h[findCell(x, y)];
const gridH = grid.cells.h[findGridCell(x, y, grid)];
const h = packH < 20 ? gridH : packH;