// module stub to store common functions for ui editors
'use strict';
restoreDefaultEvents(); // apply default viewbox events on load
// restore default viewbox events
function restoreDefaultEvents() {
svg.call(zoom);
viewbox.style('cursor', 'default').on('.drag', null).on('click', clicked).on('touchmove mousemove', moved);
legend.call(d3.drag().on('start', dragLegendBox));
}
// on viewbox click event - run function based on target
function clicked() {
const el = d3.event.target;
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
const parent = el.parentElement,
grand = parent.parentElement,
great = grand.parentElement;
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
if (grand.id === 'emblems') editEmblem();
else if (parent.id === 'rivers') editRiver();
else if (grand.id === 'routes') editRoute();
else if (el.tagName === 'tspan' && grand.parentNode.parentNode.id === 'labels') editLabel();
else if (grand.id === 'burgLabels') editBurg();
else if (grand.id === 'burgIcons') editBurg();
else if (parent.id === 'ice') editIce();
else if (parent.id === 'terrain') editReliefIcon();
else if (parent.id === 'markers') editMarker();
else if (grand.id === 'coastline') editCoastline();
else if (great.id === 'armies') editRegiment();
else if (pack.cells.t[i] === 1) {
const node = document.getElementById('island_' + pack.cells.f[i]);
editCoastline(node);
} else if (grand.id === 'lakes') editLake();
}
// clear elSelected variable
function unselect() {
restoreDefaultEvents();
if (!elSelected) return;
elSelected.call(d3.drag().on('drag', null)).attr('class', null);
debug.selectAll('*').remove();
viewbox.style('cursor', 'default');
elSelected = null;
}
// close all dialogs except stated
function closeDialogs(except = '#except') {
$('.dialog:visible')
.not(except)
.each(function () {
$(this).dialog('close');
});
}
// move brush radius circle
function moveCircle(x, y, r = 20) {
let circle = document.getElementById('brushCircle');
if (!circle) {
const html = ``;
document.getElementById('debug').insertAdjacentHTML('afterBegin', html);
} else {
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', r);
}
}
function removeCircle() {
if (document.getElementById('brushCircle')) document.getElementById('brushCircle').remove();
}
// get browser-defined fit-content
function fitContent() {
return !window.chrome ? '-moz-max-content' : 'fit-content';
}
// apply sorting behaviour for lines on Editor header click
document.querySelectorAll('.sortable').forEach(function (e) {
e.addEventListener('click', function (e) {
sortLines(this);
});
});
function sortLines(header) {
const type = header.classList.contains('alphabetically') ? 'name' : 'number';
let order = header.className.includes('-down') ? '-up' : '-down';
if (!header.className.includes('icon-sort') && type === 'name') order = '-up';
const headers = header.parentNode;
headers.querySelectorAll('div.sortable').forEach((e) => {
e.classList.forEach((c) => {
if (c.includes('icon-sort')) e.classList.remove(c);
});
});
header.classList.add('icon-sort-' + type + order);
applySorting(headers);
}
function applySorting(headers) {
const header = headers.querySelector("div[class*='icon-sort']");
if (!header) return;
const sortby = header.dataset.sortby;
const name = header.classList.contains('alphabetically');
const desc = header.className.includes('-down') ? -1 : 1;
const list = headers.nextElementSibling;
const lines = Array.from(list.children);
lines
.sort((a, b) => {
const an = name ? a.dataset[sortby] : +a.dataset[sortby];
const bn = name ? b.dataset[sortby] : +b.dataset[sortby];
return (an > bn ? 1 : an < bn ? -1 : 0) * desc;
})
.forEach((line) => list.appendChild(line));
}
function confirmationDialog(options) {
const {
title = 'Confirm action',
message = 'Are you sure you want to continue?
The action cannot be reverted',
cancel = 'Cancel',
confirm = 'Continue',
onCancel = () => {},
onConfirm = () => {}
} = options;
alertMessage.innerHTML = message;
$('#alert').dialog({
resizable: false,
title,
buttons: {
[confirm]: function () {
onConfirm();
$(this).dialog('close');
},
[cancel]: function () {
onCancel();
$(this).dialog('close');
}
}
});
}
function addBurg(point) {
const cells = pack.cells;
const x = rn(point[0], 2),
y = rn(point[1], 2);
const cell = findCell(x, point[1]);
const i = pack.burgs.length;
const culture = cells.culture[cell];
const name = Names.getCulture(culture);
const state = cells.state[cell];
const feature = cells.f[cell];
const temple = pack.states[state].form === 'Theocracy';
const population = Math.max((cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + (cell % 100) / 1000, 0.1);
const type = BurgsAndStates.getType(cell, false);
// generate emblem
const coa = COA.generate(pack.states[state].coa, 0.25, null, type);
coa.shield = COA.getShield(culture, state);
COArenderer.add('burg', i, coa, x, y);
pack.burgs.push({name, cell, x, y, state, i, culture, feature, capital: 0, port: 0, temple, population, coa, type});
cells.burg[cell] = i;
const townSize = burgIcons.select('#towns').attr('size') || 0.5;
burgIcons
.select('#towns')
.append('circle')
.attr('id', 'burg' + i)
.attr('data-id', i)
.attr('cx', x)
.attr('cy', y)
.attr('r', townSize);
burgLabels
.select('#towns')
.append('text')
.attr('id', 'burgLabel' + i)
.attr('data-id', i)
.attr('x', x)
.attr('y', y)
.attr('dy', `${townSize * -1.5}px`)
.text(name);
BurgsAndStates.defineBurgFeatures(pack.burgs[i]);
return i;
}
function moveBurgToGroup(id, g) {
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
const anchor = document.querySelector("#anchors [data-id='" + id + "']");
if (!label || !icon) {
ERROR && console.error('Cannot find label or icon elements');
return;
}
document.querySelector('#burgLabels > #' + g).appendChild(label);
document.querySelector('#burgIcons > #' + g).appendChild(icon);
const iconSize = icon.parentNode.getAttribute('size');
icon.setAttribute('r', iconSize);
label.setAttribute('dy', `${iconSize * -1.5}px`);
if (anchor) {
document.querySelector('#anchors > #' + g).appendChild(anchor);
const anchorSize = +anchor.parentNode.getAttribute('size');
anchor.setAttribute('width', anchorSize);
anchor.setAttribute('height', anchorSize);
anchor.setAttribute('x', rn(pack.burgs[id].x - anchorSize * 0.47, 2));
anchor.setAttribute('y', rn(pack.burgs[id].y - anchorSize * 0.47, 2));
}
}
function removeBurg(id) {
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
const anchor = document.querySelector("#anchors [data-id='" + id + "']");
if (label) label.remove();
if (icon) icon.remove();
if (anchor) anchor.remove();
const cells = pack.cells,
burg = pack.burgs[id];
burg.removed = true;
cells.burg[burg.cell] = 0;
if (burg.coa) {
const coaId = 'burgCOA' + id;
if (document.getElementById(coaId)) document.getElementById(coaId).remove();
emblems.select(`#burgEmblems > use[data-i='${id}']`).remove();
delete burg.coa; // remove to save data
}
}
function toggleCapital(burg) {
const state = pack.burgs[burg].state;
if (!state) {
tip('Neutral lands cannot have a capital', false, 'error');
return;
}
if (pack.burgs[burg].capital) {
tip('To change capital please assign a capital status to another burg of this state', false, 'error');
return;
}
const old = pack.states[state].capital;
// change statuses
pack.states[state].capital = burg;
pack.states[state].center = pack.burgs[burg].cell;
pack.burgs[burg].capital = 1;
pack.burgs[old].capital = 0;
moveBurgToGroup(burg, 'cities');
moveBurgToGroup(old, 'towns');
}
function togglePort(burg) {
const anchor = document.querySelector("#anchors [data-id='" + burg + "']");
if (anchor) anchor.remove();
const b = pack.burgs[burg];
if (b.port) {
b.port = 0;
return;
} // not a port anymore
const haven = pack.cells.haven[b.cell];
const port = haven ? pack.cells.f[haven] : -1;
if (!haven) tip("Port haven is not found, system won't be able to make a searoute", false, 'warn');
b.port = port;
const g = b.capital ? 'cities' : 'towns';
const group = anchors.select('g#' + g);
const size = +group.attr('size');
group
.append('use')
.attr('xlink:href', '#icon-anchor')
.attr('data-id', burg)
.attr('x', rn(b.x - size * 0.47, 2))
.attr('y', rn(b.y - size * 0.47, 2))
.attr('width', size)
.attr('height', size);
}
function toggleBurgLock(burg) {
const b = pack.burgs[burg];
b.lock = b.lock ? 0 : 1;
}
function showBurgLockTip(burg) {
const b = pack.burgs[burg];
if (b.lock) {
tip('Click to unlock burg and allow it to be change by regeneration tools');
} else {
tip('Click to lock burg and prevent changes by regeneration tools');
}
}
// draw legend box
function drawLegend(name, data) {
legend.selectAll('*').remove(); // fully redraw every time
legend.attr('data', data.join('|')); // store data
const itemsInCol = +styleLegendColItems.value;
const fontSize = +legend.attr('font-size');
const backClr = styleLegendBack.value;
const opacity = +styleLegendOpacity.value;
const lineHeight = Math.round(fontSize * 1.7);
const colorBoxSize = Math.round(fontSize / 1.7);
const colOffset = fontSize;
const vOffset = fontSize / 2;
// append items
const boxes = legend.append('g').attr('stroke-width', 0.5).attr('stroke', '#111111').attr('stroke-dasharray', 'none');
const labels = legend.append('g').attr('fill', '#000000').attr('stroke', 'none');
const columns = Math.ceil(data.length / itemsInCol);
for (let column = 0, i = 0; column < columns; column++) {
const linesInColumn = Math.ceil(data.length / columns);
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset;
for (let l = 0; l < linesInColumn && data[i]; l++, i++) {
boxes
.append('rect')
.attr('fill', data[i][1])
.attr('x', offset)
.attr('y', lineHeight + l * lineHeight + vOffset)
.attr('width', colorBoxSize)
.attr('height', colorBoxSize);
labels
.append('text')
.text(data[i][2])
.attr('x', offset + colorBoxSize * 1.6)
.attr('y', fontSize / 1.6 + lineHeight + l * lineHeight + vOffset);
}
}
// append label
const offset = colOffset + legend.node().getBBox().width / 2;
labels
.append('text')
.attr('text-anchor', 'middle')
.attr('font-weight', 'bold')
.attr('font-size', '1.2em')
.attr('id', 'legendLabel')
.text(name)
.attr('x', offset)
.attr('y', fontSize * 1.1 + vOffset / 2);
// append box
const bbox = legend.node().getBBox();
const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset;
legend.insert('rect', ':first-child').attr('id', 'legendBox').attr('x', 0).attr('y', 0).attr('width', width).attr('height', height).attr('fill', backClr).attr('fill-opacity', opacity);
fitLegendBox();
}
// fit Legend box to canvas size
function fitLegendBox() {
if (!legend.selectAll('*').size()) return;
const px = isNaN(+legend.attr('data-x')) ? 99 : legend.attr('data-x') / 100;
const py = isNaN(+legend.attr('data-y')) ? 93 : legend.attr('data-y') / 100;
const bbox = legend.node().getBBox();
const x = rn(svgWidth * px - bbox.width),
y = rn(svgHeight * py - bbox.height);
legend.attr('transform', `translate(${x},${y})`);
}
// draw legend with the same data, but using different settings
function redrawLegend() {
if (!legend.select('rect').size()) return;
const name = legend.select('#legendLabel').text();
const data = legend
.attr('data')
.split('|')
.map((l) => l.split(','));
drawLegend(name, data);
}
function dragLegendBox() {
const tr = parseTransform(this.getAttribute('transform'));
const x = +tr[0] - d3.event.x,
y = +tr[1] - d3.event.y;
const bbox = legend.node().getBBox();
d3.event.on('drag', function () {
const px = rn(((x + d3.event.x + bbox.width) / svgWidth) * 100, 2);
const py = rn(((y + d3.event.y + bbox.height) / svgHeight) * 100, 2);
const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
legend.attr('transform', transform).attr('data-x', px).attr('data-y', py);
});
}
function clearLegend() {
legend.selectAll('*').remove();
legend.attr('data', null);
}
// draw color (fill) picker
function createPicker(hatching) {
const COLORS_IN_ROW = 14;
const pos = () => tip('Drag to change the picker position');
const cl = () => tip('Click to close the picker');
const closePicker = () => container.remove();
const container = d3.select('body').append('svg').attr('id', 'pickerContainer').attr('width', '100%').attr('height', '100%');
container.append('rect').attr('width', '100%').attr('height', '100%').attr('opacity', 0).on('mousemove', cl).on('click', closePicker);
const picker = container
.append('g')
.attr('id', 'picker')
.call(
d3
.drag()
.filter(() => event.target.tagName !== 'INPUT')
.on('start', dragPicker)
);
const controls = picker.append('g').attr('id', 'pickerControls');
const h = controls.append('g');
h.append('text').attr('x', 4).attr('y', 14).text('H:');
h.append('line').attr('x1', 18).attr('y1', 10).attr('x2', 107).attr('y2', 10);
h.append('circle').attr('cx', 75).attr('cy', 10).attr('r', 5).attr('id', 'pickerH');
h.on('mousemove', () => tip('Set palette hue'));
const s = controls.append('g');
s.append('text').attr('x', 113).attr('y', 14).text('S:');
s.append('line').attr('x1', 124).attr('y1', 10).attr('x2', 206).attr('y2', 10);
s.append('circle').attr('cx', 181.4).attr('cy', 10).attr('r', 5).attr('id', 'pickerS');
s.on('mousemove', () => tip('Set palette saturation'));
const l = controls.append('g');
l.append('text').attr('x', 213).attr('y', 14).text('L:');
l.append('line').attr('x1', 226).attr('y1', 10).attr('x2', 306).attr('y2', 10);
l.append('circle').attr('cx', 282).attr('cy', 10).attr('r', 5).attr('id', 'pickerL');
l.on('mousemove', () => tip('Set palette lightness'));
controls.selectAll('line').on('click', clickPickerControl);
controls.selectAll('circle').call(d3.drag().on('start', dragPickerControl));
const spaces = picker
.append('foreignObject')
.attr('id', 'pickerSpaces')
.attr('x', 4)
.attr('y', 20)
.attr('width', 303)
.attr('height', 20)
.on('mousemove', () => tip('Color value in different color spaces. Edit to change'));
const html = `
`;
spaces.node().insertAdjacentHTML('beforeend', html);
spaces.selectAll('input').on('change', changePickerSpace);
const colors = picker.append('g').attr('id', 'pickerColors').attr('stroke', '#333333');
const clr = d3.range(COLORS_IN_ROW).map((i) => d3.hsl((i / COLORS_IN_ROW) * 360, 0.7, 0.7).hex());
clr.forEach(function (d, i) {
colors
.append('rect')
.attr('id', 'picker_' + d)
.attr('fill', d)
.attr('class', i ? '' : 'selected')
.attr('x', i * 22 + 4)
.attr('y', 40)
.attr('width', 16)
.attr('height', 16);
});
colors
.selectAll('rect')
.on('click', pickerFillClicked)
.on('mousemove', () => tip('Click to fill with the color'));
if (hatching) {
const hatches = picker.append('g').attr('id', 'pickerHatches').attr('stroke', '#333333');
d3.selectAll('g#hatching > pattern').each(function (d, i) {
hatches
.append('rect')
.attr('id', 'picker_' + this.id)
.attr('fill', 'url(#' + this.id + ')')
.attr('x', i * 22 + 4)
.attr('y', 61)
.attr('width', 16)
.attr('height', 16);
});
hatches
.selectAll('rect')
.on('click', pickerFillClicked)
.on('mousemove', () => tip('Click to fill with the hatching'));
}
// append box
const bbox = picker.node().getBBox();
const width = bbox.width + 8;
const height = bbox.height + 9;
picker.insert('rect', ':first-child').attr('x', 0).attr('y', 0).attr('width', width).attr('height', height).attr('fill', '#ffffff').attr('stroke', '#5d4651').on('mousemove', pos);
picker.insert('text', ':first-child').attr('x', 291).attr('y', -10).attr('id', 'pickerCloseText').text('✕');
picker.insert('rect', ':first-child').attr('x', 288).attr('y', -21).attr('id', 'pickerCloseRect').attr('width', 14).attr('height', 14).on('mousemove', cl).on('click', closePicker);
picker.insert('text', ':first-child').attr('x', 12).attr('y', -10).attr('id', 'pickerLabel').text('Color Picker').on('mousemove', pos);
picker.insert('rect', ':first-child').attr('x', 0).attr('y', -30).attr('width', width).attr('height', 30).attr('id', 'pickerHeader').on('mousemove', pos);
picker.attr('transform', `translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`);
}
function updateSelectedRect(fill) {
document.getElementById('picker').querySelector('rect.selected').classList.remove('selected');
document
.getElementById('picker')
.querySelector("rect[fill='" + fill.toLowerCase() + "']")
.classList.add('selected');
}
function updateSpaces() {
// hsl
const h = getPickerControl(pickerH, 360);
const s = getPickerControl(pickerS, 1);
const l = getPickerControl(pickerL, 1);
pickerHSL_H.value = rn(h);
pickerHSL_S.value = rn(s * 100); // multiplied by 100
pickerHSL_L.value = rn(l * 100); // multiplied by 100
// rgb
const rgb = d3.color(d3.hsl(h, s, l));
pickerRGB_R.value = rgb.r;
pickerRGB_G.value = rgb.g;
pickerRGB_B.value = rgb.b;
// hex
pickerHEX.value = rgb.hex();
}
function updatePickerColors() {
const colors = d3.select('#picker > #pickerColors').selectAll('rect');
const number = colors.size();
const h = getPickerControl(pickerH, 360);
const s = getPickerControl(pickerS, 1);
const l = getPickerControl(pickerL, 1);
colors.each(function (d, i) {
const clr = d3.hsl((i / number) * 180 + h, s, l).hex();
this.setAttribute('id', 'picker_' + clr);
this.setAttribute('fill', clr);
});
}
function openPicker(fill, callback, options = {allowHatching: true}) {
createPicker(options.allowHatching);
if (fill[0] === '#') {
const hsl = d3.hsl(fill);
if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360);
if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1);
if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1);
updateSpaces();
updatePickerColors();
}
updateSelectedRect(fill);
openPicker.updateFill = function () {
const selected = document.getElementById('picker').querySelector('rect.selected');
if (!selected) return;
callback(selected.getAttribute('fill'));
};
}
function setPickerControl(control, value, max) {
const min = +control.previousSibling.getAttribute('x1');
const delta = +control.previousSibling.getAttribute('x2') - min;
const percent = value / max;
control.setAttribute('cx', min + delta * percent);
}
function getPickerControl(control, max) {
const min = +control.previousSibling.getAttribute('x1');
const delta = +control.previousSibling.getAttribute('x2') - min;
const current = +control.getAttribute('cx') - min;
return (current / delta) * max;
}
function dragPicker() {
const tr = parseTransform(this.getAttribute('transform'));
const x = +tr[0] - d3.event.x,
y = +tr[1] - d3.event.y;
const picker = d3.select('#picker');
const bbox = picker.node().getBBox();
d3.event.on('drag', function () {
const px = rn(((x + d3.event.x + bbox.width) / svgWidth) * 100, 2);
const py = rn(((y + d3.event.y + bbox.height) / svgHeight) * 100, 2);
const transform = `translate(${x + d3.event.x},${y + d3.event.y})`;
picker.attr('transform', transform).attr('data-x', px).attr('data-y', py);
});
}
function pickerFillClicked() {
const fill = this.getAttribute('fill');
updateSelectedRect(fill);
openPicker.updateFill();
const hsl = d3.hsl(fill);
if (isNaN(hsl.h)) return; // not a color
setPickerControl(pickerH, hsl.h, 360);
updateSpaces();
}
function clickPickerControl() {
const min = this.getScreenCTM().e;
this.nextSibling.setAttribute('cx', d3.event.x - min);
updateSpaces();
updatePickerColors();
openPicker.updateFill();
}
function dragPickerControl() {
const min = +this.previousSibling.getAttribute('x1');
const max = +this.previousSibling.getAttribute('x2');
d3.event.on('drag', function () {
const x = Math.max(Math.min(d3.event.x, max), min);
this.setAttribute('cx', x);
updateSpaces();
updatePickerColors();
openPicker.updateFill();
});
}
function changePickerSpace() {
const valid = this.checkValidity();
if (!valid) {
tip('You must provide a correct value', false, 'error');
return;
}
const space = this.dataset.space;
const i = Array.from(this.parentNode.querySelectorAll('input')).map((input) => input.value); // inputs
const fill = space === 'hex' ? d3.rgb(this.value) : space === 'rgb' ? d3.rgb(i[0], i[1], i[2]) : d3.hsl(i[0], i[1] / 100, i[2] / 100);
const hsl = d3.hsl(fill);
if (isNaN(hsl.l)) {
tip('You must provide a correct value', false, 'error');
return;
}
if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360);
if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1);
if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1);
updateSpaces();
updatePickerColors();
openPicker.updateFill();
}
// add fogging
function fog(id, path) {
if (defs.select('#fog #' + id).size()) return;
const fadeIn = d3.transition().duration(2000).ease(d3.easeSinInOut);
if (defs.select('#fog path').size()) {
defs.select('#fog').append('path').attr('d', path).attr('id', id).attr('opacity', 0).transition(fadeIn).attr('opacity', 1);
} else {
defs.select('#fog').append('path').attr('d', path).attr('id', id).attr('opacity', 1);
const opacity = fogging.attr('opacity');
fogging.style('display', 'block').attr('opacity', 0).transition(fadeIn).attr('opacity', opacity);
}
}
// remove fogging
function unfog(id) {
let el = defs.select('#fog #' + id);
if (!id || !el.size()) el = defs.select('#fog').selectAll('path');
el.remove();
if (!defs.selectAll('#fog path').size()) fogging.style('display', 'none');
}
function getFileName(dataType) {
const formatTime = (time) => (time < 10 ? '0' + time : time);
const name = mapName.value;
const type = dataType ? dataType + ' ' : '';
const date = new Date();
const year = date.getFullYear();
const month = formatTime(date.getMonth() + 1);
const day = formatTime(date.getDate());
const hour = formatTime(date.getHours());
const minutes = formatTime(date.getMinutes());
const dateString = [year, month, day, hour, minutes].join('-');
return name + ' ' + type + dateString;
}
function downloadFile(data, name, type = 'text/plain') {
const dataBlob = new Blob([data], {type});
const url = window.URL.createObjectURL(dataBlob);
const link = document.createElement('a');
link.download = name;
link.href = url;
link.click();
window.setTimeout(() => window.URL.revokeObjectURL(url), 2000);
}
function uploadFile(el, callback) {
const fileReader = new FileReader();
fileReader.readAsText(el.files[0], 'UTF-8');
el.value = '';
fileReader.onload = (loaded) => callback(loaded.target.result);
}
function highlightElement(element) {
if (debug.select('.highlighted').size()) return; // allow only 1 highlight element simultaniosly
const box = element.getBBox();
const transform = element.getAttribute('transform') || null;
const enter = d3.transition().duration(1000).ease(d3.easeBounceOut);
const exit = d3.transition().duration(500).ease(d3.easeLinear);
const highlight = debug.append('rect').attr('x', box.x).attr('y', box.y).attr('width', box.width).attr('height', box.height).attr('transform', transform);
highlight.classed('highlighted', 1).transition(enter).style('outline-offset', '0px').transition(exit).style('outline-color', 'transparent').delay(1000).remove();
const tr = parseTransform(transform);
let x = box.x + box.width / 2;
if (tr[0]) x += tr[0];
let y = box.y + box.height / 2;
if (tr[1]) y += tr[1];
zoomTo(x, y, scale > 2 ? scale : 3, 1600);
}
function selectIcon(initial, callback) {
if (!callback) return;
$('#iconSelector').dialog();
const table = document.getElementById('iconTable');
const input = document.getElementById('iconInput');
input.value = initial;
if (!table.innerHTML) {
const icons = [
'⚔️',
'🏹',
'🐴',
'💣',
'🌊',
'🎯',
'⚓',
'🔮',
'📯',
'⚒️',
'🛡️',
'👑',
'⚜️',
'☠️',
'🎆',
'🗡️',
'🔪',
'⛏️',
'🔥',
'🩸',
'💧',
'🐾',
'🎪',
'🏰',
'🏯',
'⛓️',
'❤️',
'💘',
'💜',
'📜',
'🔔',
'🔱',
'💎',
'🌈',
'🌠',
'✨',
'💥',
'☀️',
'🌙',
'⚡',
'❄️',
'♨️',
'🎲',
'🚨',
'🌉',
'🗻',
'🌋',
'🧱',
'⚖️',
'✂️',
'🎵',
'👗',
'🎻',
'🎨',
'🎭',
'⛲',
'💉',
'📖',
'📕',
'🎁',
'💍',
'⏳',
'🕸️',
'⚗️',
'☣️',
'☢️',
'🔰',
'🎖️',
'🚩',
'🏳️',
'🏴',
'💪',
'✊',
'👊',
'🤜',
'🤝',
'🙏',
'🧙',
'🧙♀️',
'💂',
'🤴',
'🧛',
'🧟',
'🧞',
'🧝',
'👼',
'👻',
'👺',
'👹',
'🦄',
'🐲',
'🐉',
'🐎',
'🦓',
'🐺',
'🦊',
'🐱',
'🐈',
'🦁',
'🐯',
'🐅',
'🐆',
'🐕',
'🦌',
'🐵',
'🐒',
'🦍',
'🦅',
'🕊️',
'🐓',
'🦇',
'🦜',
'🐦',
'🦉',
'🐮',
'🐄',
'🐂',
'🐃',
'🐷',
'🐖',
'🐗',
'🐏',
'🐑',
'🐐',
'🐫',
'🦒',
'🐘',
'🦏',
'🐭',
'🐁',
'🐀',
'🐹',
'🐰',
'🐇',
'🦔',
'🐸',
'🐊',
'🐢',
'🦎',
'🐍',
'🐳',
'🐬',
'🦈',
'🐠',
'🐙',
'🦑',
'🐌',
'🦋',
'🐜',
'🐝',
'🐞',
'🦗',
'🕷️',
'🦂',
'🦀',
'🌳',
'🌲',
'🎄',
'🌴',
'🍂',
'🍁',
'🌵',
'☘️',
'🍀',
'🌿',
'🌱',
'🌾',
'🍄',
'🌽',
'🌸',
'🌹',
'🌻',
'🍒',
'🍏',
'🍇',
'🍉',
'🍅',
'🍓',
'🥔',
'🥕',
'🥩',
'🍗',
'🍞',
'🍻',
'🍺',
'🍲',
'🍷'
];
let row = '';
for (let i = 0; i < icons.length; i++) {
if (i % 17 === 0) row = table.insertRow((i / 17) | 0);
const cell = row.insertCell(i % 17);
cell.innerHTML = icons[i];
}
}
table.onclick = (e) => {
if (e.target.tagName === 'TD') {
input.value = e.target.innerHTML;
callback(input.value);
}
};
table.onmouseover = (e) => {
if (e.target.tagName === 'TD') tip(`Click to select ${e.target.innerHTML} icon`);
};
$('#iconSelector').dialog({
width: fitContent(),
title: 'Select Icon',
buttons: {
Apply: function () {
callback(input.value || '⠀');
$(this).dialog('close');
},
Close: function () {
callback(initial);
$(this).dialog('close');
}
}
});
}
// Calls the refresh for all currently open editors
function refreshAllEditors() {
if (document.getElementById('culturesEditorRefresh').offsetParent) culturesEditorRefresh.click();
if (document.getElementById('biomesEditorRefresh').offsetParent) biomesEditorRefresh.click();
if (document.getElementById('diplomacyEditorRefresh').offsetParent) diplomacyEditorRefresh.click();
if (document.getElementById('provincesEditorRefresh').offsetParent) provincesEditorRefresh.click();
if (document.getElementById('religionsEditorRefresh').offsetParent) religionsEditorRefresh.click();
if (document.getElementById('statesEditorRefresh').offsetParent) statesEditorRefresh.click();
if (document.getElementById('zonesEditorRefresh').offsetParent) zonesEditorRefresh.click();
if (document.getElementById('resourcesEditorRefresh').offsetParent) resourcesEditorRefresh.click();
}