Merge branch 'master' into geodata

This commit is contained in:
Tom Vogt 2019-09-08 10:35:10 +02:00
commit 5bd06e1804
13 changed files with 949 additions and 561 deletions

View file

@ -94,7 +94,7 @@ function editBurg() {
}
function createNewGroup() {
if (!this.value) {tip("Please provide a valid group name"); return;}
if (!this.value) {tip("Please provide a valid group name", false, "error"); return;}
let group = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, "");
if (Number.isFinite(+group.charAt(0))) group = "g" + group;

View file

@ -108,7 +108,7 @@ function editBurgs() {
function getCultureOptions(culture) {
let options = "";
pack.cultures.slice(1).forEach(c => options += `<option ${c.i === culture ? "selected" : ""} value="${c.i}">${c.name}</option>`);
pack.cultures.forEach(c => options += `<option ${c.i === culture ? "selected" : ""} value="${c.i}">${c.name}</option>`);
return options;
}
@ -148,7 +148,7 @@ function editBurgs() {
function changeBurgPopulation() {
const burg = +this.parentNode.dataset.id;
if (this.value == "" || isNaN(+this.value)) {
tip("Please provide an integer number", false, "error");
tip("Please provide an integer number (like 10000, not 10K)", false, "error");
this.value = si(pack.burgs[burg].population * populationRate.value * urbanization.value);
return;
}
@ -186,7 +186,7 @@ function editBurgs() {
if (!pack.burgs[burg].port) {
const haven = pack.cells.haven[pack.burgs[burg].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, "warning");
if (!haven) tip("Port haven is not found, system won't be able to make a searoute", false, "warn");
pack.burgs[burg].port = port;
const g = pack.burgs[burg].capital ? "cities" : "towns";

View file

@ -219,7 +219,7 @@ function editCultures() {
cults.select("#culture"+culture).remove();
debug.select("#cultureCenter"+culture).remove();
pack.burgs.filter(b => b.culture === culture).forEach(b => b.culture = 0);
pack.burgs.filter(b => b.culture == culture).forEach(b => b.culture = 0);
pack.cells.culture.forEach((c, i) => {if(c === culture) pack.cells.culture[i] = 0;});
pack.cultures[culture].removed = true;

View file

@ -187,22 +187,43 @@ function editDiplomacy() {
const chronicle = pack.states[0].diplomacy;
if (!chronicle.length) {tip("Relations history is blank", false, "error"); return;}
let message = `<div>`;
chronicle.forEach(e => {
message += `<div style="margin: 0.5em 0">`;
e.forEach((l, i) => message += `<div${i ? "" : " style='font-weight:bold'"}>${l}</div>`);
message += `</div>`;
let message = `<div autocorrect="off" spellcheck="false">`;
chronicle.forEach((e, d) => {
message += `<div>`;
e.forEach((l, i) => message += `<div contenteditable="true" data-id="${d}-${i}"${i ? "" : " style='font-weight:bold'"}>${l}</div>`);
message += `&#8205;</div>`;
});
alertMessage.innerHTML = message + `</div>`;
alertMessage.innerHTML = message + `</div><i id="info-line">Type to edit. Press Enter to add a new line, empty the element to remove it</i>`;
alertMessage.querySelectorAll("div[contenteditable='true']").forEach(el => el.addEventListener("input", changeReliationsHistory));
$("#alert").dialog({title: "Relations history", position: {my: "center", at: "center", of: "svg"},
buttons: {
Save: function() {
const text = this.querySelector("div").innerText.split("\n").join("\r\n");
const dataBlob = new Blob([text], {type: "text/plain"});
const url = window.URL.createObjectURL(dataBlob);
const link = document.createElement("a");
document.body.appendChild(link);
link.download = "state_relations_history" + Date.now() + ".txt";
link.href = url;
link.click();
window.setTimeout(function() {window.URL.revokeObjectURL(url);}, 2000);
},
Clear: function() {pack.states[0].diplomacy = []; $(this).dialog("close");},
Close: function() {$(this).dialog("close");}
}
});
}
function changeReliationsHistory() {
const i = this.dataset.id.split("-");
const group = pack.states[0].diplomacy[i[0]];
if (this.innerHTML === "") {
group.splice(i[1], 1);
this.remove();
} else group[i[1]] = this.innerHTML;
}
function showRelationsMatrix() {
const states = pack.states.filter(s => s.i && !s.removed);
const valid = states.map(s => s.i);

View file

@ -1,9 +1,6 @@
// Module to store general UI functions
"use strict";
// ask before closing the window
window.onbeforeunload = () => "Are you sure you want to navigate away?";
// fit full-screen map if window is resized
$(window).resize(function(e) {
mapWidthInput.value = window.innerWidth;
@ -11,6 +8,8 @@ $(window).resize(function(e) {
changeMapSize();
});
window.onbeforeunload = () => "Are you sure you want to navigate away?";
// Tooltips
const tooltip = document.getElementById("tooltip");
@ -18,16 +17,18 @@ const tooltip = document.getElementById("tooltip");
document.getElementById("dialogs").addEventListener("mousemove", showDataTip);
document.getElementById("optionsContainer").addEventListener("mousemove", showDataTip);
function tip(tip = "Tip is undefined", main = false, error = false) {
const reg = "linear-gradient(0.1turn, #ffffff00, #5e5c5c66, #ffffff00)";
const red = "linear-gradient(0.1turn, #ffffff00, #e3141499, #ffffff00)";
function tip(tip = "Tip is undefined", main, error, time) {
tooltip.innerHTML = tip;
tooltip.style.background = error ? red : reg;
if (main) tooltip.dataset.main = tip;
tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)";
if (error === "error") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)"; else
if (error === "warn") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)"; else
if (error === "success") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)";
if (main) tooltip.dataset.main = tip; // set main tip
if (time) setTimeout(tooltip.dataset.main = "", time); // clear main in some time
}
function showMainTip() {
tooltip.style.background = "linear-gradient(0.1turn, #aaffff00, #3a26264d, #ffffff00)";
tooltip.innerHTML = tooltip.dataset.main;
}
@ -228,15 +229,22 @@ function applyOption(select, option) {
// Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys
document.addEventListener("keydown", function(event) {
const active = document.activeElement.tagName;
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey;
if (key === 118) regeneratePrompt(); // "F7" for new map
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
else if (key === 9) {toggleOptions(event); event.preventDefault();} // Tab to toggle options
else if (key === 113) regeneratePrompt(); // "F2" for new map
else if (key === 117) quickSave(); // "F6" for quick save
else if (key === 120) quickLoad(); // "F9" for quick load
else if (ctrl && key === 80) saveAsImage("png"); // Ctrl + "P" to save as PNG
else if (ctrl && key === 83) saveAsImage("svg"); // Ctrl + "S" to save as SVG
else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file
else if (ctrl && key === 76) mapToLoad.click(); // Ctrl + "L" to load MAP
else if (ctrl && key === 85) mapToLoad.click(); // Ctrl + "U" to load MAP from URL
else if (ctrl && key === 76) mapToLoad.click(); // Ctrl + "L" to load MAP from local file
else if (ctrl && key === 81) toggleSaveReminder(); // Ctrl + "Q" to toggle save reminder
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
else if (shift && key === 192) console.log(pack.cells); // Shift + "`" to log cells data

View file

@ -22,22 +22,25 @@ function restoreLayers() {
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
}
// layers to be turned on; changable by user
let presets = {
"political": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar", "toggleStates"],
"cultural": ["toggleBorders", "toggleCultures", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
"heightmap": ["toggleHeight", "toggleRivers", "toggleScaleBar"],
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
"landmass": ["toggleScaleBar"]
}
restoreLayers(); // run on-load
let presets = {}; // global object
restoreCustomPresets(); // run on-load
function getDefaultPresets() {
return {
"political": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar", "toggleStates"],
"cultural": ["toggleBorders", "toggleCultures", "toggleIcons", "toggleLabels", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
"heightmap": ["toggleHeight", "toggleRivers", "toggleScaleBar"],
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
"landmass": ["toggleScaleBar"]
}
}
function restoreCustomPresets() {
presets = getDefaultPresets();
const storedPresets = JSON.parse(localStorage.getItem("presets"));
if (!storedPresets) return;
@ -45,6 +48,7 @@ function restoreCustomPresets() {
if (presets[preset]) continue;
layersPreset.add(new Option(preset, preset));
}
presets = storedPresets;
}
@ -62,34 +66,50 @@ function changePreset(preset) {
});
layersPreset.value = preset;
localStorage.setItem("preset", preset);
const isDefault = getDefaultPresets()[preset];
removePresetButton.style.display = isDefault ? "none" : "inline-block";
savePresetButton.style.display = "none";
}
function savePreset() {
// don't allow if layers should already esist as a preset
if (layersPreset.value !== "custom") {
tip(`Current layers are already saved as a "${layersPreset.selectedOptions[0].label}" preset`, false, "error");
return;
}
// add new preset
const preset = prompt("Please provide a preset name"); // preset name
if (!preset) return;
presets[preset] = Array.from(document.getElementById("mapLayers").querySelectorAll("li:not(.buttonoff)")).map(node => node.id).sort();
layersPreset.add(new Option(preset, preset, false, true));
localStorage.setItem("presets", JSON.stringify(presets));
localStorage.setItem("preset", preset);
removePresetButton.style.display = "inline-block";
savePresetButton.style.display = "none";
}
function removePreset() {
const preset = layersPreset.value;
delete presets[preset];
const index = Array.from(layersPreset.options).findIndex(o => o.value === preset);
layersPreset.options.remove(index);
layersPreset.value = "custom";
removePresetButton.style.display = "none";
savePresetButton.style.display = "inline-block";
localStorage.setItem("presets", JSON.stringify(presets));
localStorage.removeItem("preset");
}
function getCurrentPreset() {
const layers = Array.from(document.getElementById("mapLayers").querySelectorAll("li:not(.buttonoff)")).map(node => node.id).sort();
const defaultPresets = getDefaultPresets();
for (const preset in presets) {
if (JSON.stringify(presets[preset]) !== JSON.stringify(layers)) continue;
layersPreset.value = preset;
removePresetButton.style.display = defaultPresets[preset] ? "none" : "inline-block";
return;
}
layersPreset.value = "custom";
removePresetButton.style.display = "none";
savePresetButton.style.display = "inline-block";
}
function toggleHeight() {

View file

@ -4,12 +4,6 @@
$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"});
$("#mapLayers").disableSelection();
// show control elements and remove loading screen on map load
d3.select("#loading").transition().duration(5000).style("opacity", 0).remove();
d3.select("#initial").transition().duration(5000).attr("opacity", 0).remove();
d3.select("#optionsContainer").transition().duration(5000).style("opacity", 1);
d3.select("#tooltip").transition().duration(5000).style("opacity", 1);
// remove glow if tip is aknowledged
if (localStorage.getItem("disable_click_arrow_tooltip")) {
clearMainTip();
@ -677,6 +671,7 @@ optionsContent.addEventListener("input", function(event) {
else if (id === "provincesInput") provincesOutput.value = value;
else if (id === "provincesOutput") provincesOutput.value = value;
else if (id === "provincesOutput") powerOutput.value = value;
else if (id === "powerInput") powerOutput.value = value;
else if (id === "powerOutput") powerInput.value = value;
else if (id === "neutralInput") neutralOutput.value = value;
else if (id === "neutralOutput") neutralInput.value = value;
@ -722,7 +717,7 @@ function changeMapSize() {
oceanPattern.select("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
oceanLayers.select("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
fitScaleBar();
fitLegendBox();
if (window.fitLegendBox) fitLegendBox();
}
// just apply map size that was already set, apply graph size!
@ -991,26 +986,32 @@ document.getElementById("sticked").addEventListener("click", function(event) {
const id = event.target.id;
if (id === "newMapButton") regeneratePrompt();
else if (id === "saveButton") toggleSavePane();
else if (id === "loadMap") mapToLoad.click();
else if (id === "loadButton") toggleLoadPane();
else if (id === "zoomReset") resetZoom(1000);
else if (id === "quickSave") quickSave();
else if (id === "saveMap") saveMap();
else if (id === "saveSVG") saveAsImage("svg");
else if (id === "savePNG") saveAsImage("png");
else if (id === "saveGeo") saveGeoJSON();
if (id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo") toggleSavePane();
else if (id === "saveDropbox") saveDropbox();
if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo" || id === "saveDropbox") toggleSavePane();
if (id === "loadMap") mapToLoad.click();
else if (id === "quickLoad") quickLoad();
else if (id === "loadURL") loadURL();
else if (id === "loadDropbox") loadDropbox();
if (id === "quickLoad" || id === "loadURL" || id === "loadMap" || id === "loadDropbox") toggleLoadPane();
});
function regeneratePrompt() {
if (customization) {tip("Please exit the customization mode first", false, "warning"); return;}
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
if (workingTime < 15) {regenerateMap(); return;}
if (workingTime < 10) {regenerateMap(); return;}
alertMessage.innerHTML = `Are you sure you want to generate a new map?<br>
All unsaved changes made to the current map will be lost`;
$("#alert").dialog({resizable: false, title: "Generate new map",
buttons: {
Cancel: function() {$(this).dialog("close");},
Generate: regenerateMap
Generate: function() {closeDialogs(); regenerateMap();}
}
});
}
@ -1034,13 +1035,73 @@ function toggleSavePane() {
}
});
}
}
// async function saveDropbox() {
// const filename = "fantasy_map_" + Date.now() + ".map";
// const options = {
// files: [{'url': '...', 'filename': 'fantasy_map.map'}],
// success: function () {alert("Success! Files saved to your Dropbox.")},
// progress: function (progress) {console.log(progress)},
// cancel: function (cancel) {console.log(cancel)},
// error: function (error) {console.log(error)}
// };
// // working file: "https://dl.dropbox.com/s/llg93mwyonyzdmu/test.map?dl=1";
// const dataBlob = await getMapData();
// const URL = window.URL.createObjectURL(dataBlob);
// Dropbox.save(URL, filename, options);
// }
function toggleLoadPane() {
if (loadDropdown.style.display === "block") {loadDropdown.style.display = "none"; return;}
loadDropdown.style.display = "block";
}
function loadURL() {
const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
const inner = `Provide URL to a .map file:
<input id="mapURL" type="url" style="width: 254px" placeholder="https://e-cloud.com/test.map">
<br><i>Please note server should allow CORS for file to be loaded. If CORS is not allowed, save file to Dropbox and provide a direct link</i>`;
alertMessage.innerHTML = inner;
$("#alert").dialog({resizable: false, title: "Load map from URL", width: 280,
buttons: {
Load: function() {
const value = mapURL.value;
if (!pattern.test(value)) {tip("Please provide a valid URL", false, "error"); return;}
closeDialogs();
loadMapFromURL(value);
$(this).dialog("close");
},
Cancel: function() {$(this).dialog("close");}
}
});
}
// function loadDropbox() {
// const options = {
// success: function(file) {send_files(file)},
// cancel: function() {},
// linkType: "preview",
// multiselect: false,
// extensions:['.map'],
// };
// Dropbox.choose(options);
// function send_files(file) {
// const subject = "Shared File Links";
// let body = "";
// for (let i=0; i < file.length; i++) {
// body += file[i].name + "\n" + file[i].link + "\n\n";
// }
// location.href = 'mailto:coworker@example.com?Subject=' + escape(subject) + '&body='+ escape(body),'200','200';
// }
// }
// load map
document.getElementById("mapToLoad").addEventListener("change", function() {
closeDialogs();
const fileToLoad = this.files[0];
this.value = "";
closeDialogs();
uploadFile(fileToLoad);
});