feat: autosave v1.89.29

This commit is contained in:
Azgaar 2023-07-08 21:46:33 +04:00
parent 69e630b886
commit d75ac3c99d
7 changed files with 172 additions and 102 deletions

View file

@ -1690,18 +1690,6 @@
Generator settings:
</p>
<table>
<tr data-tip="Set what Generator should do on opening">
<td></td>
<td>Onload behavior</td>
<td>
<select id="onloadMap" data-stored="onloadMap">
<option value="random" selected>Generate random map</option>
<option value="saved">Open last saved map</option>
</select>
</td>
<td></td>
</tr>
<tr
data-tip="Set user interface size. Please note browser zoom also affects interface size (Ctrl + or Ctrl - to change)"
>
@ -1750,6 +1738,33 @@
</td>
</tr>
<tr data-tip="Set autosave interval in minutes. Set 0 to disable autosave. Map is saved to browser memory">
<td></td>
<td>Autosave interval</td>
<td>
<input
id="autosaveIntervalInput"
data-stored="autosaveInterval"
type="range"
min="0"
max="60"
step="1"
value="15"
/>
</td>
<td>
<input
id="autosaveIntervalOutput"
data-stored="autosaveInterval"
type="number"
min="0"
max="60"
step="1"
value="15"
/>
</td>
</tr>
<tr data-tip="Select speech synthesis voice to pronounce generated names">
<td></td>
<td>Speaker voice</td>
@ -3239,7 +3254,7 @@
style="margin-left: 0.1em"
></i>
</div>
<iframe id="mfcgPreview" sandbox="allow-scripts allow-same-origin"></iframe>
<iframe id="mfcgPreview" sandbox="allow-same-origin"></iframe>
</div>
</div>
@ -5797,7 +5812,7 @@
</button>
<button
onclick="quickSave()"
data-tip="Save the project to browser storage. It can be unreliable"
data-tip="Save the project to browser storage. It will overwrite the latest autosave"
data-shortcut="F6"
>
browser
@ -7826,9 +7841,10 @@
<script src="libs/d3.min.js"></script>
<script src="libs/priority-queue.min.js"></script>
<script src="libs/delaunator.min.js"></script>
<script src="libs/indexedDB.js"></script>
<script src="utils/shorthands.js"></script>
<script src="utils/commonUtils.js"></script>
<script src="utils/commonUtils.js?v=1.89.29"></script>
<script src="utils/arrayUtils.js"></script>
<script src="utils/colorUtils.js"></script>
<script src="utils/graphUtils.js?v=1.88.02"></script>
@ -7866,7 +7882,7 @@
<script src="modules/ui/general.js?v=1.87.03"></script>
<script src="modules/ui/options.js?v=1.89.19"></script>
<script src="main.js?v=1.89.19"></script>
<script src="main.js?v=1.89.29"></script>
<script defer src="modules/relief-icons.js"></script>
<script defer src="modules/ui/style.js"></script>
@ -7908,8 +7924,8 @@
<script defer src="libs/rgbquant.min.js"></script>
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script defer src="modules/io/save.js"></script>
<script defer src="modules/io/load.js?v=1.88.05"></script>
<script defer src="modules/io/save.js?v=1.89.29"></script>
<script defer src="modules/io/load.js?v=1.89.29"></script>
<script defer src="modules/io/cloud.js"></script>
<script defer src="modules/io/export.js?v=1.89.17"></script>
<script defer src="modules/io/formats.js"></script>

82
libs/indexedDB.js Normal file
View file

@ -0,0 +1,82 @@
function waitForIndexedDB() {
return new Promise(resolve => {
const timer = setInterval(() => {
if (window.indexedDB) {
clearInterval(timer);
resolve();
}
}, 100);
});
}
function getValue(key) {
return new Promise((resolve, reject) => {
if (!window.indexedDB) {
reject(new Error("indexedDB not supported"));
return;
}
waitForIndexedDB().then(() => {
const request = window.indexedDB.open("d2", 1);
request.onsuccess = event => {
const db = event.target.result;
const transaction = db.transaction("s", "readonly");
const objectStore = transaction.objectStore("s");
const getRequest = objectStore.get(key);
getRequest.onsuccess = event => {
const value = (event.target.result && event.target.result.v) || null;
resolve(value);
};
getRequest.onerror = event => {
reject(new Error("indexedDB request error"));
console.log(event);
};
};
request.onerror = event => {
reject(new Error("indexedDB request error"));
console.log(event);
};
});
});
}
function setValue(key, value) {
return new Promise((resolve, reject) => {
if (!window.indexedDB) {
reject(new Error("indexedDB not supported"));
return;
}
waitForIndexedDB().then(() => {
const request = window.indexedDB.open("d2", 1);
request.onsuccess = event => {
const db = event.target.result;
const transaction = db.transaction("s", "readwrite");
const objectStore = transaction.objectStore("s");
objectStore.put({k: key, v: value});
transaction.oncomplete = () => {
resolve();
};
transaction.onerror = event => {
reject(new Error("indexedDB request error"));
console.log(event);
};
};
request.onerror = event => {
reject(new Error("indexedDB request error"));
console.log(event);
};
});
});
}
window.ldb = {get: getValue, set: setValue};

33
main.js
View file

@ -221,8 +221,7 @@ oceanLayers
document.addEventListener("DOMContentLoaded", async () => {
if (!location.hostname) {
const wiki = "https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Run-FMG-locally";
alertMessage.innerHTML = /* html */ `Fantasy Map Generator cannot run serverless. Follow the <a href="${wiki}" target="_blank">instructions</a> on how you can
easily run a local web-server`;
alertMessage.innerHTML = /* html */ `Fantasy Map Generator cannot run serverless. Follow the <a href="${wiki}" target="_blank">instructions</a> on how you can easily run a local web-server`;
$("#alert").dialog({
resizable: false,
@ -240,6 +239,7 @@ document.addEventListener("DOMContentLoaded", async () => {
await checkLoadParameters();
}
restoreDefaultEvents(); // apply default viewbox events
initiateAutosave();
});
function hideLoading() {
@ -280,35 +280,20 @@ async function checkLoadParameters() {
return;
}
// open latest map if option is active and map is stored
const loadLastMap = () =>
new Promise((resolve, reject) => {
ldb.get("lastMap", blob => {
if (blob) {
WARN && console.warn("Load last saved map");
try {
uploadMap(blob);
resolve();
} catch (error) {
reject(error);
}
} else {
reject("No map stored");
}
});
});
if (onloadMap.value === "saved") {
// check if there is a map saved to indexedDB
const blob = await ldb.get("lastMap");
if (blob) {
try {
await loadLastMap();
WARN && console.warn("Loading last stored map");
uploadMap(blob);
} catch (error) {
ERROR && console.error(error);
WARN && console.warn("Cannot load stored map, random map to be generated");
await generateMapOnLoad();
generateMapOnLoad();
}
} else {
WARN && console.warn("Generate random map");
await generateMapOnLoad();
generateMapOnLoad();
}
}

View file

@ -1,15 +1,13 @@
"use strict";
// Functions to load and parse .map files
function quickLoad() {
ldb.get("lastMap", blob => {
if (blob) {
loadMapPrompt(blob);
} else {
tip("No map stored. Save map to storage first", true, "error", 2000);
ERROR && console.error("No map stored");
}
});
async function quickLoad() {
const blob = ldb.get("lastMap");
if (blob) loadMapPrompt(blob);
else {
tip("No map stored. Save map to browser storage first", true, "error", 2000);
ERROR && console.error("No map stored");
}
}
async function loadFromDropbox() {

View file

@ -3,8 +3,6 @@
// prepare map data for saving
function getMapData() {
TIME && console.time("createMapData");
const date = new Date();
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
@ -116,13 +114,13 @@ function getMapData() {
fonts,
markers
].join("\r\n");
TIME && console.timeEnd("createMapData");
return mapData;
}
// Download .map file
function dowloadMap() {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
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 = getMapData();
@ -137,7 +135,8 @@ function dowloadMap() {
}
async function saveToDropbox() {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
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 = getMapData();
const filename = getFileName() + ".map";
@ -150,12 +149,36 @@ async function saveToDropbox() {
}
}
function quickSave() {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
async function initiateAutosave() {
const MINUTE = 60000; // munite in milliseconds
let lastSavedAt = Date.now();
async function autosave() {
const timeoutMinutes = byId("autosaveIntervalOutput").valueAsNumber;
if (!timeoutMinutes) return;
const diffInMinutes = (Date.now() - lastSavedAt) / MINUTE;
if (diffInMinutes < timeoutMinutes) return;
if (customization) return tip("Autosave: map cannot be saved in edit mode", false, "warning", 2000);
tip("Autosave: saving map...", false, "warning", 3000);
const mapData = getMapData();
const blob = new Blob([mapData], {type: "text/plain"});
await ldb.set("lastMap", blob);
console.log("Autosaved at", new Date().toLocaleTimeString());
lastSavedAt = Date.now();
}
setInterval(autosave, MINUTE / 2);
}
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 = getMapData();
const blob = new Blob([mapData], {type: "text/plain"});
if (blob) 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 .map file to secure progress", true, "success", 2000);
}

View file

@ -134,7 +134,11 @@ function isCtrlClick(event) {
}
function generateDate(from = 100, to = 1000) {
return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", {year: "numeric", month: "long", day: "numeric"});
return new Date(rand(from, to), rand(12), rand(31)).toLocaleDateString("en", {
year: "numeric",
month: "long",
day: "numeric"
});
}
function getLongitude(x, decimals = 2) {
@ -158,7 +162,8 @@ void (function () {
const defaultOptions = {default: 1, step: 0.01, min: 0, max: 100, required: true};
window.prompt = function (promptText = defaultText, options = defaultOptions, callback) {
if (options.default === undefined) return ERROR && console.error("Prompt: options object does not have default value defined");
if (options.default === undefined)
return ERROR && console.error("Prompt: options object does not have default value defined");
const input = prompt.querySelector("#promptInput");
prompt.querySelector("#promptText").innerHTML = promptText;
@ -192,41 +197,3 @@ void (function () {
prompt.style.display = "none";
});
})();
// indexedDB; ldb object
void (function () {
function e(t, o) {
return n
? void (n.transaction("s").objectStore("s").get(t).onsuccess = function (e) {
var t = (e.target.result && e.target.result.v) || null;
o(t);
})
: void setTimeout(function () {
e(t, o);
}, 100);
}
var t = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
if (!t) return void ERROR && console.error("indexedDB not supported");
var n,
o = {k: "", v: ""},
r = t.open("d2", 1);
(r.onsuccess = function (e) {
n = this.result;
}),
(r.onerror = function (e) {
ERROR && console.error("indexedDB request error"), INFO && console.log(e);
}),
(r.onupgradeneeded = function (e) {
n = null;
var t = e.target.result.createObjectStore("s", {keyPath: "k"});
t.transaction.oncomplete = function (e) {
n = e.target.db;
};
}),
(window.ldb = {
get: e,
set: function (e, t) {
(o.k = e), (o.v = t), n.transaction("s", "readwrite").objectStore("s").put(o);
}
});
})();

View file

@ -1,7 +1,7 @@
"use strict";
// version and caching control
const version = "1.89.28"; // generator version, update each time
const version = "1.89.29"; // generator version, update each time
{
document.title += " v" + version;
@ -28,6 +28,7 @@ const version = "1.89.28"; // generator version, update each time
<ul>
<strong>Latest changes:</strong>
<li>Autosave feature (in Options)</li>
<li>Google translation support (in Options)</li>
<li>Religions can be edited and redrawn like cultures</li>
<li>Lock states, provinces, cultures, and religions from regeneration</li>
@ -35,8 +36,6 @@ const version = "1.89.28"; // generator version, update each time
<li>Data Charts screen</li>
<li>Сultures and religions can have multiple parents in hierarchy tree</li>
<li>Heightmap selection screen</li>
<li>Dialogs optimization for mobile</li>
<li>New heightmap template: Fractious</li>
</ul>
<p>Join our <a href="${discord}" target="_blank">Discord server</a> and <a href="${reddit}" target="_blank">Reddit community</a> to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>