collect statistics for a short period

This commit is contained in:
Azgaar 2021-09-24 01:00:03 +03:00
parent 2a9edd2458
commit 5d714c8c17
9 changed files with 160 additions and 53 deletions

View file

@ -554,12 +554,6 @@ input[type="color"]::-webkit-color-swatch-wrapper {
width: 100%; width: 100%;
} }
#optionsSeedGenerate:before {
content: "✓";
margin-left: -2px;
font-weight: bold;
}
#options input[type="color"] { #options input[type="color"] {
width: 2em; width: 2em;
padding: 1px; padding: 1px;

View file

@ -235,7 +235,7 @@
<div id="collapsible"> <div id="collapsible">
<button id="optionsTrigger" data-t="tipOptionsTrigger" data-tip="Click to show the Menu. Shortcut: Tab" class="options glow" onclick="showOptions(event)" style="padding:.6em .45em"></button> <button id="optionsTrigger" data-t="tipOptionsTrigger" data-tip="Click to show the Menu. Shortcut: Tab" class="options glow" onclick="showOptions(event)" style="padding:.6em .45em"></button>
<button id="regenerate" data-t="tipRegenerate" data-tip="Click to generate a new map. Shortcut: F2" onclick="regeneratePrompt()" class="options" style="display: none"><t data-t="newMap">New Map!</t></button> <button id="regenerate" data-t="tipRegenerate" data-tip="Click to generate a new map. Shortcut: F2" onclick="regeneratePrompt('drawer')" class="options" style="display: none"><t data-t="newMap">New Map!</t></button>
</div> </div>
<div id="options" style="display:none"> <div id="options" style="display:none">
@ -942,14 +942,13 @@
<tr data-tip="Map seed number. Seed produces the same map only if canvas size and options are the same"> <tr data-tip="Map seed number. Seed produces the same map only if canvas size and options are the same">
<td> <td>
<i data-tip="Click to generate a map for this seed" id="optionsSeedGenerate"></i> <i data-tip="Show seed history to apply a previous seed" id="optionsMapHistory" class="icon-history"></i>
</td> </td>
<td>Map seed</td> <td>Map seed</td>
<td> <td>
<input id="optionsSeed" class="long" type="number" min=1 max=999999999 step=1> <input id="optionsSeed" class="long" type="number" min=1 max=999999999 step=1>
</td> </td>
<td> <td>
<i data-tip="Show seed history to apply a previous seed" id="optionsMapHistory" class="icon-history"></i>
<i data-tip="Copy map seed as URL. It will produce the same map only if options are default or the same" id="optionsCopySeed" class="icon-docs"></i> <i data-tip="Copy map seed as URL. It will produce the same map only if options are default or the same" id="optionsCopySeed" class="icon-docs"></i>
</td> </td>
</tr> </tr>
@ -4304,6 +4303,7 @@
<script src="modules/fonts.js"></script> <script src="modules/fonts.js"></script>
<script src="modules/ui/layers.js"></script> <script src="modules/ui/layers.js"></script>
<script src="modules/ui/measurers.js"></script> <script src="modules/ui/measurers.js"></script>
<script src="libs/umami.js"></script>
<script async defer src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script> <script async defer src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script>
<script async defer src="modules/ui/general.js"></script> <script async defer src="modules/ui/general.js"></script>
@ -4354,6 +4354,5 @@
<script async defer src="libs/jquery.ui.touch-punch.min.js"></script> <script async defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script async defer src="libs/pell.min.js"></script> <script async defer src="libs/pell.min.js"></script>
<script async defer src="libs/jszip.min.js"></script> <script async defer src="libs/jszip.min.js"></script>
<script async defer data-website-id="4f6fd0ae-646a-4946-a9da-7aad63284e48" src="https://fmg-stats.herokuapp.com/umami.js"></script>
</body> </body>
</html> </html>

48
libs/umami.js Normal file
View file

@ -0,0 +1,48 @@
(window => {
const noTrack = window.localStorage.getItem("noTrack");
if (noTrack) return;
const {
screen: {width, height},
navigator: {language},
location: {hostname, pathname, search},
document: {referrer}
} = window;
const website = "4f6fd0ae-646a-4946-a9da-7aad63284e48";
const root = "https://fmg-stats.herokuapp.com";
const screen = `${width}x${height}`;
const url = `${pathname}${search}`;
const post = (url, data) => {
const req = new XMLHttpRequest();
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/json");
req.send(JSON.stringify(data));
};
const collect = (type, params) => {
const payload = {website, hostname, screen, language, cache: false};
Object.keys(params).forEach(key => {
payload[key] = params[key];
});
post(`${root}/api/collect`, {type, payload});
};
// const addEvents = () => {
// document.querySelectorAll("[class*='umami--']").forEach(element => {
// element.className.split(" ").forEach(className => {
// if (/^umami--([a-z]+)--([\w]+[\w-]*)$/.test(className)) {
// const [, type, value] = className.split("--");
// const listener = () => collect("event", {event_type: type, event_value: value});
// element.addEventListener(type, listener, true);
// }
// });
// });
// };
// addEvents();
collect("pageview", {url, referrer});
window.track = (event_type = "reach", event_value = "") => collect("event", {event_type, event_value, url});
})(window);

12
main.js
View file

@ -652,11 +652,13 @@ function generate() {
INFO && console.groupEnd("Generated Map " + seed); INFO && console.groupEnd("Generated Map " + seed);
} catch (error) { } catch (error) {
ERROR && console.error(error); ERROR && console.error(error);
const parsedError = parseError(error);
track("error", parsedError);
clearMainTip(); clearMainTip();
alertMessage.innerHTML = `An error is occured on map generation. Please retry. alertMessage.innerHTML = `An error is occured on map generation. Please retry.
<br>If error is critical, clear the stored data and try again. <br>If error is critical, clear the stored data and try again.
<p id="errorBox">${parseError(error)}</p>`; <p id="errorBox">${parsedError}</p>`;
$("#alert").dialog({ $("#alert").dialog({
resizable: false, resizable: false,
title: "Generation error", title: "Generation error",
@ -667,7 +669,7 @@ function generate() {
localStorage.setItem("version", version); localStorage.setItem("version", version);
}, },
Regenerate: function () { Regenerate: function () {
regenerateMap(); regenerateMap("generation error");
$(this).dialog("close"); $(this).dialog("close");
}, },
Ignore: function () { Ignore: function () {
@ -1830,7 +1832,7 @@ function addZones(number = 1) {
// show map stats on generation complete // show map stats on generation complete
function showStatistics() { function showStatistics() {
const template = templateInput.value; const template = templateInput.options[templateInput.selectedIndex].text;
const templateRandom = locked("template") ? "" : "(random)"; const templateRandom = locked("template") ? "" : "(random)";
const stats = ` Seed: ${seed} const stats = ` Seed: ${seed}
Canvas size: ${graphWidth}x${graphHeight} Canvas size: ${graphWidth}x${graphHeight}
@ -1848,9 +1850,10 @@ function showStatistics() {
mapId = Date.now(); // unique map id is it's creation date number mapId = Date.now(); // unique map id is it's creation date number
mapHistory.push({seed, width: graphWidth, height: graphHeight, template, created: mapId}); mapHistory.push({seed, width: graphWidth, height: graphHeight, template, created: mapId});
INFO && console.log(stats); INFO && console.log(stats);
track("generate", `Template: ${template} ${templateRandom}. Points: ${pointsInput.dataset.cells}`);
} }
const regenerateMap = debounce(function () { const regenerateMap = debounce(function (source) {
WARN && console.warn("Generate new random map"); WARN && console.warn("Generate new random map");
closeDialogs("#worldConfigurator, #options3d"); closeDialogs("#worldConfigurator, #options3d");
customization = 0; customization = 0;
@ -1860,6 +1863,7 @@ const regenerateMap = debounce(function () {
restoreLayers(); restoreLayers();
if (ThreeD.options.isOn) ThreeD.redraw(); if (ThreeD.options.isOn) ThreeD.redraw();
if ($("#worldConfigurator").is(":visible")) editWorld(); if ($("#worldConfigurator").is(":visible")) editWorld();
track("regenerate", `from ${source}`);
}, 1000); }, 1000);
// clear the map // clear the map

View file

@ -4,6 +4,7 @@
// download map as SVG // download map as SVG
async function saveSVG() { async function saveSVG() {
TIME && console.time("saveSVG"); TIME && console.time("saveSVG");
track("export", "svg");
const url = await getMapURL("svg"); const url = await getMapURL("svg");
const link = document.createElement("a"); const link = document.createElement("a");
link.download = getFileName() + ".svg"; link.download = getFileName() + ".svg";
@ -17,6 +18,7 @@ async function saveSVG() {
// download map as PNG // download map as PNG
async function savePNG() { async function savePNG() {
TIME && console.time("savePNG"); TIME && console.time("savePNG");
track("export", "png");
const url = await getMapURL("png"); const url = await getMapURL("png");
const link = document.createElement("a"); const link = document.createElement("a");
@ -47,6 +49,7 @@ async function savePNG() {
// download map as JPEG // download map as JPEG
async function saveJPEG() { async function saveJPEG() {
TIME && console.time("saveJPEG"); TIME && console.time("saveJPEG");
track("export", "jpg");
const url = await getMapURL("png"); const url = await getMapURL("png");
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
@ -264,7 +267,11 @@ async function getMapURL(type, options = {}) {
if (!cloneEl.getElementById("labels")) cloneEl.getElementById("textPaths")?.remove(); // removed unused textPaths if (!cloneEl.getElementById("labels")) cloneEl.getElementById("textPaths")?.remove(); // removed unused textPaths
// add armies style // add armies style
if (cloneEl.getElementById("armies")) cloneEl.insertAdjacentHTML("afterbegin", "<style>#armies text {stroke: none; fill: #fff; text-shadow: 0 0 4px #000; dominant-baseline: central; text-anchor: middle; font-family: Helvetica; fill-opacity: 1;}#armies text.regimentIcon {font-size: .8em;}</style>"); if (cloneEl.getElementById("armies"))
cloneEl.insertAdjacentHTML(
"afterbegin",
"<style>#armies text {stroke: none; fill: #fff; text-shadow: 0 0 4px #000; dominant-baseline: central; text-anchor: middle; font-family: Helvetica; fill-opacity: 1;}#armies text.regimentIcon {font-size: .8em;}</style>"
);
// add xlink: for href to support svg1.1 // add xlink: for href to support svg1.1
if (type === "svg") { if (type === "svg") {
@ -372,6 +379,7 @@ function inlineStyle(clone) {
} }
function saveGeoJSON_Cells() { function saveGeoJSON_Cells() {
track("export", "getJSON cells");
const json = {type: "FeatureCollection", features: []}; const json = {type: "FeatureCollection", features: []};
const cells = pack.cells; const cells = pack.cells;
const getPopulation = i => { const getPopulation = i => {
@ -402,6 +410,7 @@ function saveGeoJSON_Cells() {
} }
function saveGeoJSON_Routes() { function saveGeoJSON_Routes() {
track("export", "getJSON routes");
const json = {type: "FeatureCollection", features: []}; const json = {type: "FeatureCollection", features: []};
routes.selectAll("g > path").each(function () { routes.selectAll("g > path").each(function () {
@ -418,6 +427,7 @@ function saveGeoJSON_Routes() {
} }
function saveGeoJSON_Rivers() { function saveGeoJSON_Rivers() {
track("export", "getJSON rivers");
const json = {type: "FeatureCollection", features: []}; const json = {type: "FeatureCollection", features: []};
rivers.selectAll("path").each(function () { rivers.selectAll("path").each(function () {
@ -440,6 +450,8 @@ function saveGeoJSON_Rivers() {
} }
function saveGeoJSON_Markers() { function saveGeoJSON_Markers() {
// TODO: rework for new markers
track("export", "getJSON markers");
const json = {type: "FeatureCollection", features: []}; const json = {type: "FeatureCollection", features: []};
markers.selectAll("use").each(function () { markers.selectAll("use").each(function () {

View file

@ -13,6 +13,7 @@ function quickLoad() {
} }
async function loadFromDropbox() { async function loadFromDropbox() {
track("load", `from dropbox`);
const mapPath = document.getElementById("loadFromDropboxSelect")?.value; const mapPath = document.getElementById("loadFromDropboxSelect")?.value;
DEBUG && console.log("Loading map from Dropbox:", mapPath); DEBUG && console.log("Loading map from Dropbox:", mapPath);
@ -68,6 +69,7 @@ function loadMapPrompt(blob) {
function loadLastSavedMap() { function loadLastSavedMap() {
WARN && console.warn("Load last saved map"); WARN && console.warn("Load last saved map");
track("load", `from browser storage`);
try { try {
uploadMap(blob); uploadMap(blob);
} catch (error) { } catch (error) {
@ -78,6 +80,7 @@ function loadMapPrompt(blob) {
} }
function loadMapFromURL(maplink, random) { function loadMapFromURL(maplink, random) {
track("load", `from url`);
const URL = decodeURIComponent(maplink); const URL = decodeURIComponent(maplink);
fetch(URL, {method: "GET", mode: "cors"}) fetch(URL, {method: "GET", mode: "cors"})
@ -93,6 +96,7 @@ function loadMapFromURL(maplink, random) {
} }
function showUploadErrorMessage(error, URL, random) { function showUploadErrorMessage(error, URL, random) {
track("error", `map load from url`);
ERROR && console.error(error); ERROR && console.error(error);
alertMessage.innerHTML = `Cannot load map from the ${link(URL, "link provided")}. alertMessage.innerHTML = `Cannot load map from the ${link(URL, "link provided")}.
${random ? `A new random map is generated. ` : ""} ${random ? `A new random map is generated. ` : ""}
@ -431,12 +435,27 @@ function parseLoadedData(data) {
// 1.0 adds a legend box // 1.0 adds a legend box
legend = svg.append("g").attr("id", "legend"); legend = svg.append("g").attr("id", "legend");
legend.attr("font-family", "Almendra SC").attr("font-size", 13).attr("data-size", 13).attr("data-x", 99).attr("data-y", 93).attr("stroke-width", 2.5).attr("stroke", "#812929").attr("stroke-dasharray", "0 4 10 4").attr("stroke-linecap", "round"); legend
.attr("font-family", "Almendra SC")
.attr("font-size", 13)
.attr("data-size", 13)
.attr("data-x", 99)
.attr("data-y", 93)
.attr("stroke-width", 2.5)
.attr("stroke", "#812929")
.attr("stroke-dasharray", "0 4 10 4")
.attr("stroke-linecap", "round");
// 1.0 separated drawBorders fron drawStates() // 1.0 separated drawBorders fron drawStates()
stateBorders = borders.append("g").attr("id", "stateBorders"); stateBorders = borders.append("g").attr("id", "stateBorders");
provinceBorders = borders.append("g").attr("id", "provinceBorders"); provinceBorders = borders.append("g").attr("id", "provinceBorders");
borders.attr("opacity", null).attr("stroke", null).attr("stroke-width", null).attr("stroke-dasharray", null).attr("stroke-linecap", null).attr("filter", null); borders
.attr("opacity", null)
.attr("stroke", null)
.attr("stroke-width", null)
.attr("stroke-dasharray", null)
.attr("stroke-linecap", null)
.attr("filter", null);
stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt"); stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt"); provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
@ -976,7 +995,7 @@ function parseLoadedData(data) {
}, },
"New map": function () { "New map": function () {
$(this).dialog("close"); $(this).dialog("close");
regenerateMap(); regenerateMap("loading error");
}, },
Cancel: function () { Cancel: function () {
$(this).dialog("close"); $(this).dialog("close");

View file

@ -16,12 +16,15 @@ function editHeightmap() {
width: "28em", width: "28em",
buttons: { buttons: {
Erase: function () { Erase: function () {
track("edit", "heightmap erase");
enterHeightmapEditMode("erase"); enterHeightmapEditMode("erase");
}, },
Keep: function () { Keep: function () {
track("edit", "heightmap keep");
enterHeightmapEditMode("keep"); enterHeightmapEditMode("keep");
}, },
Risk: function () { Risk: function () {
track("edit", "heightmap risk");
enterHeightmapEditMode("risk"); enterHeightmapEditMode("risk");
}, },
Cancel: function () { Cancel: function () {
@ -87,7 +90,16 @@ function editHeightmap() {
exitCustomization.style.bottom = svgHeight / 2 + "px"; exitCustomization.style.bottom = svgHeight / 2 + "px";
exitCustomization.style.transform = "scale(2)"; exitCustomization.style.transform = "scale(2)";
exitCustomization.style.display = "block"; exitCustomization.style.display = "block";
d3.select("#exitCustomization").transition().duration(1000).style("opacity", 1).transition().duration(2000).ease(d3.easeSinInOut).style("right", "10px").style("bottom", "10px").style("transform", "scale(1)"); d3.select("#exitCustomization")
.transition()
.duration(1000)
.style("opacity", 1)
.transition()
.duration(2000)
.ease(d3.easeSinInOut)
.style("right", "10px")
.style("bottom", "10px")
.style("transform", "scale(1)");
} else exitCustomization.style.display = "block"; } else exitCustomization.style.display = "block";
openBrushesPanel(); openBrushesPanel();
@ -130,7 +142,8 @@ function editHeightmap() {
// Exit customization mode // Exit customization mode
function finalizeHeightmap() { function finalizeHeightmap() {
if (viewbox.select("#heights").selectAll("*").size() < 200) return tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error"); if (viewbox.select("#heights").selectAll("*").size() < 200)
return tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error");
if (document.getElementById("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error"); if (document.getElementById("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error");
delete window.edits; // remove global variable delete window.edits; // remove global variable
@ -618,7 +631,10 @@ function editHeightmap() {
else if (brush === "brushLower") s.forEach(i => (h[i] = lim(h[i] - power))); else if (brush === "brushLower") s.forEach(i => (h[i] = lim(h[i] - power)));
else if (brush === "brushDepress") s.forEach((i, d) => (h[i] = lim(h[i] - interpolate(d / Math.max(s.length - 1, 1))))); else if (brush === "brushDepress") s.forEach((i, d) => (h[i] = lim(h[i] - interpolate(d / Math.max(s.length - 1, 1)))));
else if (brush === "brushAlign") s.forEach(i => (h[i] = lim(h[start]))); else if (brush === "brushAlign") s.forEach(i => (h[i] = lim(h[start])));
else if (brush === "brushSmooth") s.forEach(i => (h[i] = rn((d3.mean(grid.cells.c[i].filter(i => (land ? h[i] >= 20 : 1)).map(c => h[c])) + h[i] * (10 - power) + 0.6) / (11 - power), 1))); else if (brush === "brushSmooth")
s.forEach(
i => (h[i] = rn((d3.mean(grid.cells.c[i].filter(i => (land ? h[i] >= 20 : 1)).map(c => h[c])) + h[i] * (10 - power) + 0.6) / (11 - power), 1))
);
else if (brush === "brushDisrupt") s.forEach(i => (h[i] = h[i] < 15 ? h[i] : lim(h[i] + power / 1.6 - Math.random() * power))); else if (brush === "brushDisrupt") s.forEach(i => (h[i] = h[i] < 15 ? h[i] : lim(h[i] + power / 1.6 - Math.random() * power)));
mockHeightmapSelection(s); mockHeightmapSelection(s);
@ -688,6 +704,7 @@ function editHeightmap() {
function openTemplateEditor() { function openTemplateEditor() {
if ($("#templateEditor").is(":visible")) return; if ($("#templateEditor").is(":visible")) return;
track("edit", "template editor");
const body = document.getElementById("templateBody"); const body = document.getElementById("templateBody");
$("#templateEditor").dialog({ $("#templateEditor").dialog({
@ -767,15 +784,29 @@ function editHeightmap() {
const TempY = `<span>y:<input class="templateY" data-tip="Placement range percentage along Y axis (minY-maxY)" value=${arg5 || "20-80"}></span>`; const TempY = `<span>y:<input class="templateY" data-tip="Placement range percentage along Y axis (minY-maxY)" value=${arg5 || "20-80"}></span>`;
const TempX = `<span>x:<input class="templateX" data-tip="Placement range percentage along X axis (minX-maxX)" value=${arg4 || "15-85"}></span>`; const TempX = `<span>x:<input class="templateX" data-tip="Placement range percentage along X axis (minX-maxX)" value=${arg4 || "15-85"}></span>`;
const Height = `<span>h:<input class="templateHeight" data-tip="Blob maximum height, use hyphen to get a random number in range" value=${arg3 || "40-50"}></span>`; const Height = `<span>h:<input class="templateHeight" data-tip="Blob maximum height, use hyphen to get a random number in range" value=${
arg3 || "40-50"
}></span>`;
const Count = `<span>n:<input class="templateCount" data-tip="Blobs to add, use hyphen to get a random number in range" value=${count || "1-2"}></span>`; const Count = `<span>n:<input class="templateCount" data-tip="Blobs to add, use hyphen to get a random number in range" value=${count || "1-2"}></span>`;
const blob = `${common}${TempY}${TempX}${Height}${Count}</div>`; const blob = `${common}${TempY}${TempX}${Height}${Count}</div>`;
if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return blob; if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return blob;
if (type === "Strait") return `${common}<span>d:<select class="templateDist" data-tip="Strait direction"><option value="vertical" selected>vertical</option><option value="horizontal">horizontal</option></select></span><span>w:<input class="templateCount" data-tip="Strait width, use hyphen to get a random number in range" value=${count || "2-7"}></span></div>`; if (type === "Strait")
if (type === "Add") return `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells"><option value="all" selected>all cells</option><option value="land">land only</option><option value="interval">interval</option></select></span><span>v:<input class="templateCount" data-tip="Add value to height of all cells (negative values are allowed)" type="number" value=${count || -10} min=-100 max=100 step=1></span></div>`; return `${common}<span>d:<select class="templateDist" data-tip="Strait direction"><option value="vertical" selected>vertical</option><option value="horizontal">horizontal</option></select></span><span>w:<input class="templateCount" data-tip="Strait width, use hyphen to get a random number in range" value=${
if (type === "Multiply") return `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells"><option value="all" selected>all cells</option><option value="land">land only</option><option value="interval">interval</option></select></span><span>v:<input class="templateCount" data-tip="Multiply all cells Height by the value" type="number" value=${count || 1.1} min=0 max=10 step=.1></span></div>`; count || "2-7"
if (type === "Smooth") return `${common}<span>f:<input class="templateCount" data-tip="Set smooth fraction. 1 - full smooth, 2 - half-smooth, etc." type="number" min=1 max=10 value=${count || 2}></span></div>`; }></span></div>`;
if (type === "Add")
return `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells"><option value="all" selected>all cells</option><option value="land">land only</option><option value="interval">interval</option></select></span><span>v:<input class="templateCount" data-tip="Add value to height of all cells (negative values are allowed)" type="number" value=${
count || -10
} min=-100 max=100 step=1></span></div>`;
if (type === "Multiply")
return `${common}<span>to:<select class="templateDist" data-tip="Change only land or all cells"><option value="all" selected>all cells</option><option value="land">land only</option><option value="interval">interval</option></select></span><span>v:<input class="templateCount" data-tip="Multiply all cells Height by the value" type="number" value=${
count || 1.1
} min=0 max=10 step=.1></span></div>`;
if (type === "Smooth")
return `${common}<span>f:<input class="templateCount" data-tip="Set smooth fraction. 1 - full smooth, 2 - half-smooth, etc." type="number" min=1 max=10 value=${
count || 2
}></span></div>`;
} }
function setRange(event) { function setRange(event) {
@ -910,6 +941,7 @@ function editHeightmap() {
function openImageConverter() { function openImageConverter() {
if ($("#imageConverter").is(":visible")) return; if ($("#imageConverter").is(":visible")) return;
track("edit", "convert image");
imageToLoad.click(); imageToLoad.click();
closeDialogs("#imageConverter"); closeDialogs("#imageConverter");
@ -1170,10 +1202,14 @@ function editHeightmap() {
} }
function setConvertColorsNumber() { function setConvertColorsNumber() {
prompt(`Please set maximum number of colors. <br>An actual number is usually lower and depends on color scheme`, {default: +convertColors.value, step: 1, min: 3, max: 255}, number => { prompt(
`Please set maximum number of colors. <br>An actual number is usually lower and depends on color scheme`,
{default: +convertColors.value, step: 1, min: 3, max: 255},
number => {
convertColors.value = number; convertColors.value = number;
heightsFromImage(number); heightsFromImage(number);
}); }
);
} }
function setOverlayOpacity(v) { function setOverlayOpacity(v) {

View file

@ -26,7 +26,7 @@ function handleKeyup(event) {
const alt = altKey || key === "Alt"; const alt = altKey || key === "Alt";
if (key === "F1") showInfo(); if (key === "F1") showInfo();
else if (key === "F2") regeneratePrompt(); else if (key === "F2") regeneratePrompt("hotkey");
else if (key === "F6") quickSave(); else if (key === "F6") quickSave();
else if (key === "F9") quickLoad(); else if (key === "F9") quickLoad();
else if (key === "TAB") toggleOptions(event); else if (key === "TAB") toggleOptions(event);

View file

@ -13,6 +13,7 @@ if (localStorage.getItem("disable_click_arrow_tooltip")) {
// Show options pane on trigger click // Show options pane on trigger click
function showOptions(event) { function showOptions(event) {
track("click", "show options");
if (!localStorage.getItem("disable_click_arrow_tooltip")) { if (!localStorage.getItem("disable_click_arrow_tooltip")) {
clearMainTip(); clearMainTip();
localStorage.setItem("disable_click_arrow_tooltip", true); localStorage.setItem("disable_click_arrow_tooltip", true);
@ -75,6 +76,7 @@ document
// show popup with a list of Patreon supportes (updated manually, to be replaced with API call) // show popup with a list of Patreon supportes (updated manually, to be replaced with API call)
function showSupporters() { function showSupporters() {
track("click", "show supporters");
const supporters = `Aaron Meyer,Ahmad Amerih,AstralJacks,aymeric,Billy Dean Goehring,Branndon Edwards,Chase Mayers,Curt Flood,cyninge,Dino Princip, const supporters = `Aaron Meyer,Ahmad Amerih,AstralJacks,aymeric,Billy Dean Goehring,Branndon Edwards,Chase Mayers,Curt Flood,cyninge,Dino Princip,
E.M. White,es,Fondue,Fritjof Olsson,Gatsu,Johan Fröberg,Jonathan Moore,Joseph Miranda,Kate,KC138,Luke Nelson,Markus Finster,Massimo Vella,Mikey, E.M. White,es,Fondue,Fritjof Olsson,Gatsu,Johan Fröberg,Jonathan Moore,Joseph Miranda,Kate,KC138,Luke Nelson,Markus Finster,Massimo Vella,Mikey,
Nathan Mitchell,Paavi1,Pat,Ryan Westcott,Sasquatch,Shawn Spencer,Sizz_TV,Timothée CALLET,UTG community,Vlad Tomash,Wil Sisney,William Merriott, Nathan Mitchell,Paavi1,Pat,Ryan Westcott,Sasquatch,Shawn Spencer,Sizz_TV,Timothée CALLET,UTG community,Vlad Tomash,Wil Sisney,William Merriott,
@ -160,7 +162,7 @@ optionsContent.addEventListener("change", function (event) {
const value = event.target.value; const value = event.target.value;
if (id === "zoomExtentMin" || id === "zoomExtentMax") changeZoomExtent(value); if (id === "zoomExtentMin" || id === "zoomExtentMax") changeZoomExtent(value);
else if (id === "optionsSeed") generateMapWithSeed(); else if (id === "optionsSeed") generateMapWithSeed("seed change");
else if (id === "uiSizeInput" || id === "uiSizeOutput") changeUIsize(value); else if (id === "uiSizeInput" || id === "uiSizeOutput") changeUIsize(value);
if (id === "shapeRendering") viewbox.attr("shape-rendering", value); if (id === "shapeRendering") viewbox.attr("shape-rendering", value);
else if (id === "yearInput") changeYear(); else if (id === "yearInput") changeYear();
@ -170,7 +172,6 @@ optionsContent.addEventListener("change", function (event) {
optionsContent.addEventListener("click", function (event) { optionsContent.addEventListener("click", function (event) {
const id = event.target.id; const id = event.target.id;
if (id === "toggleFullscreen") toggleFullscreen(); if (id === "toggleFullscreen") toggleFullscreen();
else if (id === "optionsSeedGenerate") generateMapWithSeed();
else if (id === "optionsMapHistory") showSeedHistoryDialog(); else if (id === "optionsMapHistory") showSeedHistoryDialog();
else if (id === "optionsCopySeed") copyMapURL(); else if (id === "optionsCopySeed") copyMapURL();
else if (id === "optionsEraRegenerate") regenerateEra(); else if (id === "optionsEraRegenerate") regenerateEra();
@ -211,8 +212,8 @@ function changeMapSize() {
// just apply canvas size that was already set // just apply canvas size that was already set
function applyMapSize() { function applyMapSize() {
const zoomMin = +zoomExtentMin.value, const zoomMin = +zoomExtentMin.value;
zoomMax = +zoomExtentMax.value; const zoomMax = +zoomExtentMax.value;
graphWidth = +mapWidthInput.value; graphWidth = +mapWidthInput.value;
graphHeight = +mapHeightInput.value; graphHeight = +mapHeightInput.value;
svgWidth = Math.min(graphWidth, window.innerWidth); svgWidth = Math.min(graphWidth, window.innerWidth);
@ -280,12 +281,9 @@ function testSpeaker() {
speechSynthesis.speak(speaker); speechSynthesis.speak(speaker);
} }
function generateMapWithSeed() { function generateMapWithSeed(source) {
if (optionsSeed.value == seed) { if (optionsSeed.value == seed) return tip("The current map already has this seed", false, "error");
tip("The current map already has this seed", false, "error"); regeneratePrompt(source);
return;
}
regeneratePrompt();
} }
function showSeedHistoryDialog() { function showSeedHistoryDialog() {
@ -316,7 +314,7 @@ function restoreSeed(id) {
mapHeightInput.value = mapHistory[id].height; mapHeightInput.value = mapHistory[id].height;
templateInput.value = mapHistory[id].template; templateInput.value = mapHistory[id].template;
if (locked("template")) unlock("template"); if (locked("template")) unlock("template");
regeneratePrompt(); regeneratePrompt("seed history");
} }
function restoreDefaultZoomExtent() { function restoreDefaultZoomExtent() {
@ -656,23 +654,17 @@ function restoreDefaultOptions() {
// Sticked menu Options listeners // Sticked menu Options listeners
document.getElementById("sticked").addEventListener("click", function (event) { document.getElementById("sticked").addEventListener("click", function (event) {
const id = event.target.id; const id = event.target.id;
if (id === "newMapButton") regeneratePrompt(); if (id === "newMapButton") regeneratePrompt("sticky button");
else if (id === "saveButton") showSavePane(); else if (id === "saveButton") showSavePane();
else if (id === "exportButton") showExportPane(); else if (id === "exportButton") showExportPane();
else if (id === "loadButton") showLoadPane(); else if (id === "loadButton") showLoadPane();
else if (id === "zoomReset") resetZoom(1000); else if (id === "zoomReset") resetZoom(1000);
}); });
function regeneratePrompt() { function regeneratePrompt(source) {
if (customization) { if (customization) return tip("New map cannot be generated when edit mode is active, please exit the mode and retry", false, "error");
tip("New map cannot be generated when edit mode is active, please exit the mode and retry", false, "error");
return;
}
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
if (workingTime < 5) { if (workingTime < 5) return regenerateMap(source);
regenerateMap();
return;
}
alertMessage.innerHTML = `Are you sure you want to generate a new map?<br> alertMessage.innerHTML = `Are you sure you want to generate a new map?<br>
All unsaved changes made to the current map will be lost`; All unsaved changes made to the current map will be lost`;
@ -685,7 +677,7 @@ function regeneratePrompt() {
}, },
Generate: function () { Generate: function () {
closeDialogs(); closeDialogs();
regenerateMap(); regenerateMap(source);
} }
} }
}); });
@ -792,6 +784,7 @@ function loadURL() {
// load map // load map
document.getElementById("mapToLoad").addEventListener("change", function () { document.getElementById("mapToLoad").addEventListener("change", function () {
track("load", `from local file`);
const fileToLoad = this.files[0]; const fileToLoad = this.files[0];
this.value = ""; this.value = "";
closeDialogs(); closeDialogs();
@ -811,6 +804,7 @@ function openSaveTiles() {
width: "23em", width: "23em",
buttons: { buttons: {
Download: function () { Download: function () {
track("export", `tiles`);
status.innerHTML = "Preparing for download..."; status.innerHTML = "Preparing for download...";
setTimeout(() => (status.innerHTML = "Downloading. It may take some time."), 1000); setTimeout(() => (status.innerHTML = "Downloading. It may take some time."), 1000);
loading = setInterval(() => (status.innerHTML += "."), 1000); loading = setInterval(() => (status.innerHTML += "."), 1000);
@ -900,6 +894,7 @@ function enterStandardView() {
} }
async function enter3dView(type) { async function enter3dView(type) {
track("click", `3d mode: ${type}`);
const canvas = document.createElement("canvas"); const canvas = document.createElement("canvas");
canvas.id = "canvas3d"; canvas.id = "canvas3d";
canvas.dataset.type = type; canvas.dataset.type = type;