refactor: cleanup, change wording

This commit is contained in:
Azgaar 2023-08-15 13:15:24 +04:00
parent ef24e3ea1a
commit 2424c66475
8 changed files with 83 additions and 68 deletions

View file

@ -2330,7 +2330,7 @@
<button id="newMapButton" data-tip="Generate a new map based on options" data-shortcut="F2">New Map</button> <button id="newMapButton" data-tip="Generate a new map based on options" data-shortcut="F2">New Map</button>
<button id="exportButton" data-tip="Select format to download image or export map data">Export</button> <button id="exportButton" data-tip="Select format to download image or export map data">Export</button>
<button id="saveButton" data-tip="Save fully-functional map in .gz format">Save</button> <button id="saveButton" data-tip="Save fully-functional map in .gz format">Save</button>
<button id="loadButton" data-tip="Load fully-functional map in .gz or .map format">Load</button> <button id="loadButton" data-tip="Load fully-functional map (.gz or .map formats)">Load</button>
<button id="zoomReset" data-tip="Reset map zoom" data-shortcut="0 (zero)">Reset Zoom</button> <button id="zoomReset" data-tip="Reset map zoom" data-shortcut="0 (zero)">Reset Zoom</button>
</div> </div>
</div> </div>
@ -5884,10 +5884,10 @@
<div id="saveMapData" style="display: none" class="dialog"> <div id="saveMapData" style="display: none" class="dialog">
<div style="margin-top: 0.3em"> <div style="margin-top: 0.3em">
<strong>Save map to</strong> <strong>Save map to</strong>
<button onclick="downloadMap()" data-tip="Download .gz file to your local disk" data-shortcut="Ctrl + S"> <button onclick="downloadMap()" data-tip="Download map file to your local disk" data-shortcut="Ctrl + S">
machine machine
</button> </button>
<button onclick="saveToDropbox()" data-tip="Save .gz file to your Dropbox" data-shortcut="Ctrl + C"> <button onclick="saveToDropbox()" data-tip="Save map file to your Dropbox" data-shortcut="Ctrl + C">
dropbox dropbox
</button> </button>
<button <button
@ -5900,16 +5900,22 @@
</div> </div>
<p> <p>
Maps are saved in <i>.gz</i> format, that can be loaded back via the <i>Load</i> in menu. There is no way to Maps are saved in <i>.gz</i> format, that can be loaded back via the <i>Load</i> in menu. There is no way to
restore the progress if file is lost. Please keep old <i>.gz or .map</i> files on your machine or cloud storage as restore the progress if file is lost. Please keep old save files on your machine or cloud storage as backups.
backups.
</p> </p>
</div> </div>
<div id="loadMapData" style="display: none" class="dialog"> <div id="loadMapData" style="display: none" class="dialog">
<div> <div>
<strong>Load map from</strong> <strong>Load map from</strong>
<button onclick="mapToLoad.click()" data-tip="Load .gz or .map file from your local disk">machine</button> <button onclick="mapToLoad.click()" data-tip="Load map file (.gz or .map) from your local disk">
<button onclick="loadURL()" data-tip="Load .gz or .map file from URL (server should allow CORS)">URL</button> machine
</button>
<button
onclick="loadURL()"
data-tip="Load map file (.gz or .map) file from URL. Note that the server should allow CORS"
>
URL
</button>
<button onclick="quickLoad()" data-tip="Load map from browser storage (if saved before)">storage</button> <button onclick="quickLoad()" data-tip="Load map from browser storage (if saved before)">storage</button>
</div> </div>
<div id="loadFromDropbox"> <div id="loadFromDropbox">
@ -5926,7 +5932,7 @@
<select id="loadFromDropboxSelect" style="width: 22em"></select> <select id="loadFromDropboxSelect" style="width: 22em"></select>
<div id="loadFromDropboxButtons" style="margin-bottom: 0.6em"> <div id="loadFromDropboxButtons" style="margin-bottom: 0.6em">
<button onclick="loadFromDropbox()" data-tip="Load .gz or .map file from your Dropbox">Load</button> <button onclick="loadFromDropbox()" data-tip="Load map file (.gz or .map) from your Dropbox">Load</button>
<button <button
onclick="createSharableDropboxLink()" onclick="createSharableDropboxLink()"
data-tip="Select file and create a link to share with your friends" data-tip="Select file and create a link to share with your friends"
@ -5995,7 +6001,7 @@
<div id="resampleDialog" style="display: none" class="dialog"> <div id="resampleDialog" style="display: none" class="dialog">
<div style="width: 34em; max-width: 80vw; font-weight: bold; padding: 6px"> <div style="width: 34em; max-width: 80vw; font-weight: bold; padding: 6px">
This operation is destructive and irreversible. It will create a completely new map based on the current one. This operation is destructive and irreversible. It will create a completely new map based on the current one.
Don't forget to save the current project as a .gz file first! Don't forget to save the current project to your machine first!
</div> </div>
<div <div
@ -6116,7 +6122,7 @@
data-main="Сlick the arrow button for options. Zoom in to see the map in details" data-main="Сlick the arrow button for options. Zoom in to see the map in details"
></div> ></div>
<div id="mapOverlay" style="display: none">Drop a .gz or .map file to open</div> <div id="mapOverlay" style="display: none">Drop a map file to open</div>
<div id="fileInputs" style="display: none"> <div id="fileInputs" style="display: none">
<input type="file" accept=".map,.gz" id="mapToLoad" /> <input type="file" accept=".map,.gz" id="mapToLoad" />

View file

@ -574,9 +574,10 @@ void (function addDragToUpload() {
overlay.style.display = "none"; overlay.style.display = "none";
if (e.dataTransfer.items == null || e.dataTransfer.items.length !== 1) return; // no files or more than one if (e.dataTransfer.items == null || e.dataTransfer.items.length !== 1) return; // no files or more than one
const file = e.dataTransfer.items[0].getAsFile(); const file = e.dataTransfer.items[0].getAsFile();
if (!file.name.endsWith(".map") && !file.name.endsWith(".gz")) { if (!file.name.endsWith(".map") && !file.name.endsWith(".gz")) {
// not a .gz/.map file alertMessage.innerHTML =
alertMessage.innerHTML = "Please upload a <b>.gz or .map</b> file you have previously downloaded"; "Please upload a map file (<i>.gz</i> or <i>.map</i> formats) you have previously downloaded";
$("#alert").dialog({ $("#alert").dialog({
resizable: false, resizable: false,
title: "Invalid file format", title: "Invalid file format",
@ -596,7 +597,7 @@ void (function addDragToUpload() {
if (closeDialogs) closeDialogs(); if (closeDialogs) closeDialogs();
uploadMap(file, () => { uploadMap(file, () => {
overlay.style.display = "none"; overlay.style.display = "none";
overlay.innerHTML = "Drop a .gz or .map file to open"; overlay.innerHTML = "Drop a map file to open";
}); });
}); });
})(); })();

View file

@ -1,6 +1,6 @@
"use strict"; "use strict";
// update old .gz/.map version to the current one // update old map file to the current version
export function resolveVersionConflicts(version) { export function resolveVersionConflicts(version) {
if (version < 1) { if (version < 1) {
// v1.0 added a new religions layer // v1.0 added a new religions layer

View file

@ -76,7 +76,7 @@ function loadMapPrompt(blob) {
function loadMapFromURL(maplink, random) { function loadMapFromURL(maplink, random) {
const URL = decodeURIComponent(maplink); const URL = decodeURIComponent(maplink);
fetch(URL, { method: "GET", mode: "cors" }) fetch(URL, {method: "GET", mode: "cors"})
.then(response => { .then(response => {
if (response.ok) return response.blob(); if (response.ok) return response.blob();
throw new Error("Cannot load map from URL"); throw new Error("Cannot load map from URL");
@ -90,8 +90,9 @@ function loadMapFromURL(maplink, random) {
function showUploadErrorMessage(error, URL, random) { function showUploadErrorMessage(error, URL, random) {
ERROR && console.error(error); ERROR && console.error(error);
alertMessage.innerHTML = /* html */ `Cannot load map from the ${link(URL, "link provided")}. ${random ? `A new random map is generated. ` : "" alertMessage.innerHTML = /* html */ `Cannot load map from the ${link(URL, "link provided")}. ${
} Please ensure the random ? `A new random map is generated. ` : ""
} Please ensure the
linked file is reachable and CORS is allowed on server side`; linked file is reachable and CORS is allowed on server side`;
$("#alert").dialog({ $("#alert").dialog({
title: "Loading error", title: "Loading error",
@ -131,35 +132,38 @@ function uploadMap(file, callback) {
fileReader.readAsArrayBuffer(file); fileReader.readAsArrayBuffer(file);
} }
async function uncompressMapData(compressedMapData) {
console.log("trying to uncompress:", compressedMapData); async function uncompress(compressedData) {
try { try {
const uncompressedStream = new Blob([compressedMapData]).stream().pipeThrough(new DecompressionStream("gzip")); const uncompressedStream = new Blob([compressedData]).stream().pipeThrough(new DecompressionStream("gzip"));
let uncompressedData = []; let uncompressedData = [];
for await (const chunk of uncompressedStream) { for await (const chunk of uncompressedStream) {
uncompressedData = uncompressedData.concat(Array.from(chunk)); uncompressedData = uncompressedData.concat(Array.from(chunk));
} }
return new Uint8Array(uncompressedData); return new Uint8Array(uncompressedData);
} } catch (error) {
catch (error) { ERROR && console.error(error);
console.error(error);
return null; return null;
} }
} }
async function parseLoadedResult(result) { async function parseLoadedResult(result) {
try { try {
const resultAsString = new TextDecoder().decode(result); const resultAsString = new TextDecoder().decode(result);
// data can be in FMG internal format or base64 encoded // data can be in FMG internal format or base64 encoded
const isDelimited = resultAsString.substring(0, 10).includes("|"); const isDelimited = resultAsString.substring(0, 10).includes("|");
const decoded = isDelimited ? resultAsString : decodeURIComponent(atob(resultAsString)); const decoded = isDelimited ? resultAsString : decodeURIComponent(atob(resultAsString));
const mapData = decoded.split("\r\n"); const mapData = decoded.split("\r\n");
const mapVersion = parseFloat(mapData[0].split("|")[0] || mapData[0]); const mapVersion = parseFloat(mapData[0].split("|")[0] || mapData[0]);
return [mapData, mapVersion]; return [mapData, mapVersion];
} catch (error) { } catch (error) {
const uncompressedData = await uncompressMapData(result); // map file can be compressed with gzip
if (uncompressedData !== null) { const uncompressedData = await uncompress(result);
return parseLoadedResult(uncompressedData); if (uncompressedData) return parseLoadedResult(uncompressedData);
}
ERROR && console.error(error); ERROR && console.error(error);
return [null, null]; return [null, null];
} }
@ -170,7 +174,7 @@ function showUploadMessage(type, mapData, mapVersion) {
let message, title, canBeLoaded; let message, title, canBeLoaded;
if (type === "invalid") { if (type === "invalid") {
message = `The file does not look like a valid <i>.gz or .map</i> file.<br>Please check the data format`; message = `The file does not look like a valid save file.<br>Please check the data format`;
title = "Invalid file"; title = "Invalid file";
canBeLoaded = false; canBeLoaded = false;
} else if (type === "ancient") { } else if (type === "ancient") {
@ -194,7 +198,7 @@ function showUploadMessage(type, mapData, mapVersion) {
if (canBeLoaded) parseLoadedData(mapData); if (canBeLoaded) parseLoadedData(mapData);
} }
}; };
$("#alert").dialog({ title, buttons }); $("#alert").dialog({title, buttons});
} }
async function parseLoadedData(data) { async function parseLoadedData(data) {
@ -263,9 +267,9 @@ async function parseLoadedData(data) {
if (data[34]) { if (data[34]) {
const usedFonts = JSON.parse(data[34]); const usedFonts = JSON.parse(data[34]);
usedFonts.forEach(usedFont => { usedFonts.forEach(usedFont => {
const { family: usedFamily, unicodeRange: usedRange, variant: usedVariant } = usedFont; const {family: usedFamily, unicodeRange: usedRange, variant: usedVariant} = usedFont;
const defaultFont = fonts.find( const defaultFont = fonts.find(
({ family, unicodeRange, variant }) => ({family, unicodeRange, variant}) =>
family === usedFamily && unicodeRange === usedRange && variant === usedVariant family === usedFamily && unicodeRange === usedRange && variant === usedVariant
); );
if (!defaultFont) fonts.push(usedFont); if (!defaultFont) fonts.push(usedFont);
@ -348,7 +352,7 @@ async function parseLoadedData(data) {
void (function parseGridData() { void (function parseGridData() {
grid = JSON.parse(data[6]); grid = JSON.parse(data[6]);
const { cells, vertices } = calculateVoronoi(grid.points, grid.boundary); const {cells, vertices} = calculateVoronoi(grid.points, grid.boundary);
grid.cells = cells; grid.cells = cells;
grid.vertices = vertices; grid.vertices = vertices;
@ -366,7 +370,7 @@ async function parseLoadedData(data) {
pack.cultures = JSON.parse(data[13]); pack.cultures = JSON.parse(data[13]);
pack.states = JSON.parse(data[14]); pack.states = JSON.parse(data[14]);
pack.burgs = JSON.parse(data[15]); pack.burgs = JSON.parse(data[15]);
pack.religions = data[29] ? JSON.parse(data[29]) : [{ i: 0, name: "No religion" }]; pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: "No religion"}];
pack.provinces = data[30] ? JSON.parse(data[30]) : [0]; pack.provinces = data[30] ? JSON.parse(data[30]) : [0];
pack.rivers = data[32] ? JSON.parse(data[32]) : []; pack.rivers = data[32] ? JSON.parse(data[32]) : [];
pack.markers = data[35] ? JSON.parse(data[35]) : []; pack.markers = data[35] ? JSON.parse(data[35]) : [];
@ -392,7 +396,7 @@ async function parseLoadedData(data) {
const e = d.split("|"); const e = d.split("|");
if (!e.length) return; if (!e.length) return;
const b = e[5].split(",").length > 2 || !nameBases[i] ? e[5] : nameBases[i].b; const b = e[5].split(",").length > 2 || !nameBases[i] ? e[5] : nameBases[i].b;
nameBases[i] = { name: e[0], min: e[1], max: e[2], d: e[3], m: e[4], b }; nameBases[i] = {name: e[0], min: e[1], max: e[2], d: e[3], m: e[4], b};
}); });
} }
})(); })();
@ -641,7 +645,7 @@ async function parseLoadedData(data) {
$(this).dialog("close"); $(this).dialog("close");
} }
}, },
position: { my: "center", at: "center", of: "svg" } position: {my: "center", at: "center", of: "svg"}
}); });
} }
} }

View file

@ -116,25 +116,28 @@ function getMapData() {
].join("\r\n"); ].join("\r\n");
return mapData; return mapData;
} }
async function compressMapData(mapData){
const compressedStream = new Blob([mapData]).stream().pipeThrough(new CompressionStream("gzip")); async function compressData(uncompressedData) {
const compressedStream = new Blob([uncompressedData]).stream().pipeThrough(new CompressionStream("gzip"));
let compressedData = []; let compressedData = [];
for await (const chunk of compressedStream){ for await (const chunk of compressedStream) {
compressedData = compressedData.concat(Array.from(chunk)); compressedData = compressedData.concat(Array.from(chunk));
} }
return new Uint8Array(compressedData); return new Uint8Array(compressedData);
} }
// download .gz file
// Download .gz file
async function downloadMap() { async function downloadMap() {
if (customization) if (customization)
return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
closeDialogs("#alert"); closeDialogs("#alert");
const mapData = await compressMapData(getMapData()); const compressedMapData = await compressData(getMapData());
const blob = new Blob([mapData], {type: "text/plain"}); const blob = new Blob([compressedMapData], {type: "text/plain"});
const URL = window.URL.createObjectURL(blob); const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a"); const link = document.createElement("a");
link.download = getFileName() + ".gz"; link.download = getFileName() + ".gz";
link.href = URL; link.href = URL;
@ -147,10 +150,12 @@ async function saveToDropbox() {
if (customization) if (customization)
return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
closeDialogs("#alert"); closeDialogs("#alert");
const mapData = await compressMapData(getMapData());
const compressedMapData = await compressData(getMapData());
const filename = getFileName() + ".gz"; const filename = getFileName() + ".gz";
try { try {
await Cloud.providers.dropbox.save(filename, mapData); await Cloud.providers.dropbox.save(filename, compressedMapData);
tip("Map is saved to your Dropbox", true, "success", 8000); tip("Map is saved to your Dropbox", true, "success", 8000);
} catch (msg) { } catch (msg) {
ERROR && console.error(msg); ERROR && console.error(msg);
@ -171,8 +176,8 @@ async function initiateAutosave() {
if (customization) return tip("Autosave: map cannot be saved in edit mode", false, "warning", 2000); if (customization) return tip("Autosave: map cannot be saved in edit mode", false, "warning", 2000);
tip("Autosave: saving map...", false, "warning", 3000); tip("Autosave: saving map...", false, "warning", 3000);
const mapData = await compressMapData(getMapData()); const compressedMapData = await compressData(getMapData());
const blob = new Blob([mapData], {type: "text/plain"}); const blob = new Blob([compressedMapData], {type: "text/plain"});
await ldb.set("lastMap", blob); await ldb.set("lastMap", blob);
INFO && console.log("Autosaved at", new Date().toLocaleTimeString()); INFO && console.log("Autosaved at", new Date().toLocaleTimeString());
lastSavedAt = Date.now(); lastSavedAt = Date.now();
@ -185,23 +190,23 @@ async function quickSave() {
if (customization) if (customization)
return tip("Map cannot be saved when edit mode is active, please exit the mode first", false, "error"); return tip("Map cannot be saved when edit mode is active, please exit the mode first", false, "error");
const mapData = await compressMapData(getMapData()); const compressedMapData = await compressData(getMapData());
const blob = new Blob([mapData], {type: "text/plain"}); const blob = new Blob([compressedMapData], {type: "text/plain"});
await ldb.set("lastMap", blob); // auto-save map await ldb.set("lastMap", blob); // auto-save map
tip("Map is saved to browser memory. Please also save as .gz file to secure progress", true, "success", 2000); tip("Map is saved to browser memory. Please also save to your desktop to secure the progress", true, "success", 2000);
} }
const saveReminder = function () { const saveReminder = function () {
if (localStorage.getItem("noReminder")) return; if (localStorage.getItem("noReminder")) return;
const message = [ const message = [
"Please don't forget to save your work as a .gz file", "Please don't forget to save the project to desktop from time to time",
"Please remember to save work as a .gz file", "Please remember to save the map to your desktop",
"Saving in .gz format will ensure your data won't be lost in case of issues", "Saving will ensure your data won't be lost in case of issues",
"Safety is number one priority. Please save the map", "Safety is number one priority. Please save the map",
"Don't forget to save your map on a regular basis!", "Don't forget to save your map on a regular basis!",
"Just a gentle reminder for you to save the map", "Just a gentle reminder for you to save the map",
"Please don't forget to save your progress (saving as .gz is the best option)", "Please don't forget to save your progress (saving to desktop is the best option)",
"Don't want to be reminded about need to save? Press CTRL+Q" "Don't want to get reminded about need to save? Press CTRL+Q"
]; ];
const interval = 15 * 60 * 1000; // remind every 15 minutes const interval = 15 * 60 * 1000; // remind every 15 minutes

View file

@ -844,7 +844,7 @@ async function connectToDropbox() {
function loadURL() { function loadURL() {
const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
const inner = `Provide URL to a .gz or .map file: const inner = `Provide URL to map file:
<input id="mapURL" type="url" style="width: 24em" placeholder="https://e-cloud.com/test.gz"> <input id="mapURL" type="url" style="width: 24em" placeholder="https://e-cloud.com/test.gz">
<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>`; <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; alertMessage.innerHTML = inner;

View file

@ -15,19 +15,18 @@ if (Array.prototype.flat === undefined) {
}; };
} }
// polyfill readable stream iterator: https://bugs.chromium.org/p/chromium/issues/detail?id=929585#c10 // readable stream iterator: https://bugs.chromium.org/p/chromium/issues/detail?id=929585#c10
if(ReadableStream.prototype[Symbol.asyncIterator] === undefined){ if (ReadableStream.prototype[Symbol.asyncIterator] === undefined) {
ReadableStream.prototype[Symbol.asyncIterator] = async function* () { ReadableStream.prototype[Symbol.asyncIterator] = async function* () {
const reader = this.getReader() const reader = this.getReader();
try { try {
while (true) { while (true) {
const {done, value} = await reader.read() const {done, value} = await reader.read();
if (done) return if (done) return;
yield value yield value;
} }
} finally {
reader.releaseLock();
} }
finally { };
reader.releaseLock() }
}
}
}

View file

@ -23,12 +23,12 @@ const version = "1.93.00"; // generator version, update each time
const discord = "https://discordapp.com/invite/X7E84HU"; const discord = "https://discordapp.com/invite/X7E84HU";
const patreon = "https://www.patreon.com/azgaar"; const patreon = "https://www.patreon.com/azgaar";
alertMessage.innerHTML = /* html */ `The Fantasy Map Generator is updated up to version <strong>${version}</strong>. This version is compatible with <a href="${changelog}" target="_blank">previous versions</a>, loaded <i>.gz or .map</i> files will be auto-updated. alertMessage.innerHTML = /* html */ `The Fantasy Map Generator is updated up to version <strong>${version}</strong>. This version is compatible with <a href="${changelog}" target="_blank">previous versions</a>, loaded save files will be auto-updated.
${storedVersion ? "<span>Reload the page to fetch fresh code.</span>" : ""} ${storedVersion ? "<span>Reload the page to fetch fresh code.</span>" : ""}
<ul> <ul>
<strong>Latest changes:</strong> <strong>Latest changes:</strong>
<li>Map save file extension is changed to .gz, .map files are still loadable</li> <li>Save files compression (file extension is changed to <i>.gz</i>). Old <i>.map</i> files are still supported</li>
<li>New label placement algorithm for states</li> <li>New label placement algorithm for states</li>
<li>North and South Poles temperature can be set independently</li> <li>North and South Poles temperature can be set independently</li>
<li>More than 70 new heraldic charges</li> <li>More than 70 new heraldic charges</li>