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!
-
Drop a .gz or .map file to open
+
Drop a map file to open
diff --git a/main.js b/main.js
index ab36463b..560ef801 100644
--- a/main.js
+++ b/main.js
@@ -574,9 +574,10 @@ void (function addDragToUpload() {
overlay.style.display = "none";
if (e.dataTransfer.items == null || e.dataTransfer.items.length !== 1) return; // no files or more than one
const file = e.dataTransfer.items[0].getAsFile();
+
if (!file.name.endsWith(".map") && !file.name.endsWith(".gz")) {
- // not a .gz/.map file
- alertMessage.innerHTML = "Please upload a .gz or .map file you have previously downloaded";
+ alertMessage.innerHTML =
+ "Please upload a map file (.gz or .map formats) you have previously downloaded";
$("#alert").dialog({
resizable: false,
title: "Invalid file format",
@@ -596,7 +597,7 @@ void (function addDragToUpload() {
if (closeDialogs) closeDialogs();
uploadMap(file, () => {
overlay.style.display = "none";
- overlay.innerHTML = "Drop a .gz or .map file to open";
+ overlay.innerHTML = "Drop a map file to open";
});
});
})();
diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js
index 99ff277a..b88e7d43 100644
--- a/modules/dynamic/auto-update.js
+++ b/modules/dynamic/auto-update.js
@@ -1,6 +1,6 @@
"use strict";
-// update old .gz/.map version to the current one
+// update old map file to the current version
export function resolveVersionConflicts(version) {
if (version < 1) {
// v1.0 added a new religions layer
diff --git a/modules/io/load.js b/modules/io/load.js
index ce662d0a..afb81435 100644
--- a/modules/io/load.js
+++ b/modules/io/load.js
@@ -76,7 +76,7 @@ function loadMapPrompt(blob) {
function loadMapFromURL(maplink, random) {
const URL = decodeURIComponent(maplink);
- fetch(URL, { method: "GET", mode: "cors" })
+ fetch(URL, {method: "GET", mode: "cors"})
.then(response => {
if (response.ok) return response.blob();
throw new Error("Cannot load map from URL");
@@ -90,8 +90,9 @@ function loadMapFromURL(maplink, random) {
function showUploadErrorMessage(error, URL, random) {
ERROR && console.error(error);
- alertMessage.innerHTML = /* html */ `Cannot load map from the ${link(URL, "link provided")}. ${random ? `A new random map is generated. ` : ""
- } Please ensure the
+ alertMessage.innerHTML = /* html */ `Cannot load map from the ${link(URL, "link provided")}. ${
+ random ? `A new random map is generated. ` : ""
+ } Please ensure the
linked file is reachable and CORS is allowed on server side`;
$("#alert").dialog({
title: "Loading error",
@@ -131,35 +132,38 @@ function uploadMap(file, callback) {
fileReader.readAsArrayBuffer(file);
}
-async function uncompressMapData(compressedMapData) {
- console.log("trying to uncompress:", compressedMapData);
+
+async function uncompress(compressedData) {
try {
- const uncompressedStream = new Blob([compressedMapData]).stream().pipeThrough(new DecompressionStream("gzip"));
+ const uncompressedStream = new Blob([compressedData]).stream().pipeThrough(new DecompressionStream("gzip"));
+
let uncompressedData = [];
for await (const chunk of uncompressedStream) {
uncompressedData = uncompressedData.concat(Array.from(chunk));
}
+
return new Uint8Array(uncompressedData);
- }
- catch (error) {
- console.error(error);
+ } catch (error) {
+ ERROR && console.error(error);
return null;
}
}
+
async function parseLoadedResult(result) {
try {
const resultAsString = new TextDecoder().decode(result);
// data can be in FMG internal format or base64 encoded
const isDelimited = resultAsString.substring(0, 10).includes("|");
const decoded = isDelimited ? resultAsString : decodeURIComponent(atob(resultAsString));
+
const mapData = decoded.split("\r\n");
const mapVersion = parseFloat(mapData[0].split("|")[0] || mapData[0]);
return [mapData, mapVersion];
} catch (error) {
- const uncompressedData = await uncompressMapData(result);
- if (uncompressedData !== null) {
- return parseLoadedResult(uncompressedData);
- }
+ // map file can be compressed with gzip
+ const uncompressedData = await uncompress(result);
+ if (uncompressedData) return parseLoadedResult(uncompressedData);
+
ERROR && console.error(error);
return [null, null];
}
@@ -170,7 +174,7 @@ function showUploadMessage(type, mapData, mapVersion) {
let message, title, canBeLoaded;
if (type === "invalid") {
- message = `The file does not look like a valid .gz or .map file. Please check the data format`;
+ message = `The file does not look like a valid save file. Please check the data format`;
title = "Invalid file";
canBeLoaded = false;
} else if (type === "ancient") {
@@ -194,7 +198,7 @@ function showUploadMessage(type, mapData, mapVersion) {
if (canBeLoaded) parseLoadedData(mapData);
}
};
- $("#alert").dialog({ title, buttons });
+ $("#alert").dialog({title, buttons});
}
async function parseLoadedData(data) {
@@ -263,9 +267,9 @@ async function parseLoadedData(data) {
if (data[34]) {
const usedFonts = JSON.parse(data[34]);
usedFonts.forEach(usedFont => {
- const { family: usedFamily, unicodeRange: usedRange, variant: usedVariant } = usedFont;
+ const {family: usedFamily, unicodeRange: usedRange, variant: usedVariant} = usedFont;
const defaultFont = fonts.find(
- ({ family, unicodeRange, variant }) =>
+ ({family, unicodeRange, variant}) =>
family === usedFamily && unicodeRange === usedRange && variant === usedVariant
);
if (!defaultFont) fonts.push(usedFont);
@@ -348,7 +352,7 @@ async function parseLoadedData(data) {
void (function parseGridData() {
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.vertices = vertices;
@@ -366,7 +370,7 @@ async function parseLoadedData(data) {
pack.cultures = JSON.parse(data[13]);
pack.states = JSON.parse(data[14]);
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.rivers = data[32] ? JSON.parse(data[32]) : [];
pack.markers = data[35] ? JSON.parse(data[35]) : [];
@@ -392,7 +396,7 @@ async function parseLoadedData(data) {
const e = d.split("|");
if (!e.length) return;
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");
}
},
- position: { my: "center", at: "center", of: "svg" }
+ position: {my: "center", at: "center", of: "svg"}
});
}
}
diff --git a/modules/io/save.js b/modules/io/save.js
index d5f2a939..de2bd876 100644
--- a/modules/io/save.js
+++ b/modules/io/save.js
@@ -116,25 +116,28 @@ function getMapData() {
].join("\r\n");
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 = [];
- for await (const chunk of compressedStream){
+ for await (const chunk of compressedStream) {
compressedData = compressedData.concat(Array.from(chunk));
}
+
return new Uint8Array(compressedData);
}
-
-// Download .gz file
+// download .gz file
async function downloadMap() {
if (customization)
return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
closeDialogs("#alert");
- const mapData = await compressMapData(getMapData());
- const blob = new Blob([mapData], {type: "text/plain"});
+ const compressedMapData = await compressData(getMapData());
+ const blob = new Blob([compressedMapData], {type: "text/plain"});
const URL = window.URL.createObjectURL(blob);
+
const link = document.createElement("a");
link.download = getFileName() + ".gz";
link.href = URL;
@@ -147,10 +150,12 @@ async function saveToDropbox() {
if (customization)
return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
closeDialogs("#alert");
- const mapData = await compressMapData(getMapData());
+
+ const compressedMapData = await compressData(getMapData());
const filename = getFileName() + ".gz";
+
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);
} catch (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);
tip("Autosave: saving map...", false, "warning", 3000);
- const mapData = await compressMapData(getMapData());
- const blob = new Blob([mapData], {type: "text/plain"});
+ const compressedMapData = await compressData(getMapData());
+ const blob = new Blob([compressedMapData], {type: "text/plain"});
await ldb.set("lastMap", blob);
INFO && console.log("Autosaved at", new Date().toLocaleTimeString());
lastSavedAt = Date.now();
@@ -185,23 +190,23 @@ async function quickSave() {
if (customization)
return tip("Map cannot be saved when edit mode is active, please exit the mode first", false, "error");
- const mapData = await compressMapData(getMapData());
- const blob = new Blob([mapData], {type: "text/plain"});
+ const compressedMapData = await compressData(getMapData());
+ const blob = new Blob([compressedMapData], {type: "text/plain"});
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 () {
if (localStorage.getItem("noReminder")) return;
const message = [
- "Please don't forget to save your work as a .gz file",
- "Please remember to save work as a .gz file",
- "Saving in .gz format will ensure your data won't be lost in case of issues",
+ "Please don't forget to save the project to desktop from time to time",
+ "Please remember to save the map to your desktop",
+ "Saving will ensure your data won't be lost in case of issues",
"Safety is number one priority. Please save the map",
"Don't forget to save your map on a regular basis!",
"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)",
- "Don't want to be reminded about need to save? Press CTRL+Q"
+ "Please don't forget to save your progress (saving to desktop is the best option)",
+ "Don't want to get reminded about need to save? Press CTRL+Q"
];
const interval = 15 * 60 * 1000; // remind every 15 minutes
diff --git a/modules/ui/options.js b/modules/ui/options.js
index ff2003a1..7321ed15 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -844,7 +844,7 @@ async function connectToDropbox() {
function loadURL() {
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:
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`;
alertMessage.innerHTML = inner;
diff --git a/utils/polyfills.js b/utils/polyfills.js
index fc567c1e..667d81ab 100644
--- a/utils/polyfills.js
+++ b/utils/polyfills.js
@@ -15,19 +15,18 @@ if (Array.prototype.flat === undefined) {
};
}
-// polyfill readable stream iterator: https://bugs.chromium.org/p/chromium/issues/detail?id=929585#c10
-if(ReadableStream.prototype[Symbol.asyncIterator] === undefined){
+// readable stream iterator: https://bugs.chromium.org/p/chromium/issues/detail?id=929585#c10
+if (ReadableStream.prototype[Symbol.asyncIterator] === undefined) {
ReadableStream.prototype[Symbol.asyncIterator] = async function* () {
- const reader = this.getReader()
+ const reader = this.getReader();
try {
while (true) {
- const {done, value} = await reader.read()
- if (done) return
- yield value
+ const {done, value} = await reader.read();
+ if (done) return;
+ yield value;
}
+ } finally {
+ reader.releaseLock();
}
- finally {
- reader.releaseLock()
- }
- }
-}
\ No newline at end of file
+ };
+}
diff --git a/versioning.js b/versioning.js
index 7d07e077..8a5287a5 100644
--- a/versioning.js
+++ b/versioning.js
@@ -23,12 +23,12 @@ const version = "1.93.00"; // generator version, update each time
const discord = "https://discordapp.com/invite/X7E84HU";
const patreon = "https://www.patreon.com/azgaar";
- alertMessage.innerHTML = /* html */ `The Fantasy Map Generator is updated up to version ${version}. This version is compatible with previous versions, loaded .gz or .map files will be auto-updated.
+ alertMessage.innerHTML = /* html */ `The Fantasy Map Generator is updated up to version ${version}. This version is compatible with previous versions, loaded save files will be auto-updated.
${storedVersion ? "Reload the page to fetch fresh code." : ""}
Latest changes:
-
Map save file extension is changed to .gz, .map files are still loadable
+
Save files compression (file extension is changed to .gz). Old .map files are still supported
New label placement algorithm for states
North and South Poles temperature can be set independently