Merge remote-tracking branch 'upstream/master' into dev-submap

This commit is contained in:
Mészáros Gergely 2021-09-11 01:12:33 +02:00
commit 1d1cd0bb71
15 changed files with 594 additions and 607 deletions

View file

@ -1,7 +1,7 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" dir="ltr"> <html lang="en" dir="ltr">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<script type="text/javascript" src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script> <script type="text/javascript" src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script>
<title>FMG Dropbox Auth</title> <title>FMG Dropbox Auth</title>
</head> </head>
@ -13,33 +13,36 @@
window. window.
*/ */
const REDIRECT_URI = window.location.origin + window.location.pathname; const REDIRECT_URI = window.location.origin + window.location.pathname;
const dbxAuth = new Dropbox.DropboxAuth({ clientId: 'sp7tzwm27u2w5ns', }); const dbxAuth = new Dropbox.DropboxAuth({clientId: "pdr9ae64ip0qno4"});
const spObj = new URLSearchParams(window.location.search); const spObj = new URLSearchParams(window.location.search);
const searchParams = Object.fromEntries(spObj.entries()) const searchParams = Object.fromEntries(spObj.entries());
if (searchParams.code) getToken() if (searchParams.code) getToken();
else doAuth(); // start authentication else doAuth(); // start authentication
function doAuth() { function doAuth() {
dbxAuth.getAuthenticationUrl(REDIRECT_URI, undefined, 'code', 'offline', undefined, undefined, true) dbxAuth
.then(authUrl => { .getAuthenticationUrl(REDIRECT_URI, undefined, "code", "offline", undefined, undefined, true)
.then(authUrl => {
window.sessionStorage.clear(); window.sessionStorage.clear();
window.sessionStorage.setItem("codeVerifier", dbxAuth.codeVerifier); window.sessionStorage.setItem("codeVerifier", dbxAuth.codeVerifier);
window.location.href = authUrl; window.location.href = authUrl;
}) })
.catch((error) => console.error(error)); .catch(error => console.error(error));
}; }
function getToken() { function getToken() {
dbxAuth.setCodeVerifier(window.sessionStorage.getItem('codeVerifier')); dbxAuth.setCodeVerifier(window.sessionStorage.getItem("codeVerifier"));
dbxAuth.getAccessTokenFromCode(REDIRECT_URI, searchParams.code) dbxAuth
.then((resp) => { .getAccessTokenFromCode(REDIRECT_URI, searchParams.code)
.then(resp => {
const token = resp.result.access_token; const token = resp.result.access_token;
window.opener.Cloud.providers.dropbox.setDropBoxToken(token) window.opener.Cloud.providers.dropbox.setDropBoxToken(token);
}).catch((error) => { })
console.error(error) .catch(error => {
}); console.error(error);
});
} }
</script> </script>
</body> </body>

175
fonts.css
View file

@ -1,175 +0,0 @@
@font-face {
font-family: 'Amatic SC';
font-style: normal;
font-weight: 700;
src: local('Amatic SC Bold'), local('AmaticSC-Bold'), url(https://fonts.gstatic.com/s/amaticsc/v11/TUZ3zwprpvBS1izr_vOMscGKfrUC.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Architects Daughter';
font-style: normal;
font-weight: 400;
src: local('Architects Daughter Regular'), local('ArchitectsDaughter-Regular'), url(https://fonts.gstatic.com/s/architectsdaughter/v8/RXTgOOQ9AAtaVOHxx0IUBM3t7GjCYufj5TXV5VnA2p8.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Bitter';
font-style: normal;
font-weight: 400;
src: local('Bitter Regular'), local('Bitter-Regular'), url(https://fonts.gstatic.com/s/bitter/v12/zfs6I-5mjWQ3nxqccMoL2A.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Caesar Dressing';
font-style: normal;
font-weight: 400;
src: local('Caesar Dressing'), local('CaesarDressing-Regular'), url(https://fonts.gstatic.com/s/caesardressing/v6/yYLx0hLa3vawqtwdswbotmK4vrRHdrz7.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Cinzel';
font-style: normal;
font-weight: 400;
src: local('Cinzel Regular'), local('Cinzel-Regular'), url(https://fonts.gstatic.com/s/cinzel/v7/zOdksD_UUTk1LJF9z4tURA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Comfortaa';
font-style: normal;
font-weight: 700;
src: local('Comfortaa Bold'), local('Comfortaa-Bold'), url(https://fonts.gstatic.com/s/comfortaa/v12/fND5XPYKrF2tQDwwfWZJI-gdm0LZdjqr5-oayXSOefg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Dancing Script';
font-style: normal;
font-weight: 700;
src: local('Dancing Script Bold'), local('DancingScript-Bold'), url(https://fonts.gstatic.com/s/dancingscript/v9/KGBfwabt0ZRLA5W1ywjowUHdOuSHeh0r6jGTOGdAKHA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Fredericka the Great';
font-style: normal;
font-weight: 400;
src: local('Fredericka the Great'), local('FrederickatheGreat'), url(https://fonts.gstatic.com/s/frederickathegreat/v6/9Bt33CxNwt7aOctW2xjbCstzwVKsIBVV--Sjxbc.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Gloria Hallelujah';
font-style: normal;
font-weight: 400;
src: local('Gloria Hallelujah'), local('GloriaHallelujah'), url(https://fonts.gstatic.com/s/gloriahallelujah/v9/CA1k7SlXcY5kvI81M_R28cNDay8z-hHR7F16xrcXsJw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Great Vibes';
font-style: normal;
font-weight: 400;
src: local('Great Vibes'), local('GreatVibes-Regular'), url(https://fonts.gstatic.com/s/greatvibes/v5/6q1c0ofG6NKsEhAc2eh-3Y4P5ICox8Kq3LLUNMylGO4.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'IM Fell English';
font-style: normal;
font-weight: 400;
src: local('IM FELL English Roman'), local('IM_FELL_English_Roman'), url(https://fonts.gstatic.com/s/imfellenglish/v7/xwIisCqGFi8pff-oa9uSVAkYLEKE0CJQa8tfZYc_plY.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Kaushan Script';
font-style: normal;
font-weight: 400;
src: local('Kaushan Script'), local('KaushanScript-Regular'), url(https://fonts.gstatic.com/s/kaushanscript/v6/qx1LSqts-NtiKcLw4N03IEd0sm1ffa_JvZxsF_BEwQk.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'MedievalSharp';
font-style: normal;
font-weight: 400;
src: local('MedievalSharp'), url(https://fonts.gstatic.com/s/medievalsharp/v9/EvOJzAlL3oU5AQl2mP5KdgptMqhwMg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Metamorphous';
font-style: normal;
font-weight: 400;
src: local('Metamorphous'), url(https://fonts.gstatic.com/s/metamorphous/v7/Wnz8HA03aAXcC39ZEX5y133EOyqs.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Montez';
font-style: normal;
font-weight: 400;
src: local('Montez Regular'), local('Montez-Regular'), url(https://fonts.gstatic.com/s/montez/v8/aq8el3-0osHIcFK6bXAPkw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Nova Script';
font-style: normal;
font-weight: 400;
src: local('Nova Script Regular'), local('NovaScript-Regular'), url(https://fonts.gstatic.com/s/novascript/v10/7Au7p_IpkSWSTWaFWkumvlQKGFw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Orbitron';
font-style: normal;
font-weight: 400;
src: local('Orbitron Regular'), local('Orbitron-Regular'), url(https://fonts.gstatic.com/s/orbitron/v9/HmnHiRzvcnQr8CjBje6GQvesZW2xOQ-xsNqO47m55DA.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Satisfy';
font-style: normal;
font-weight: 400;
src: local('Satisfy Regular'), local('Satisfy-Regular'), url(https://fonts.gstatic.com/s/satisfy/v8/2OzALGYfHwQjkPYWELy-cw.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Shadows Into Light';
font-style: normal;
font-weight: 400;
src: local('Shadows Into Light'), local('ShadowsIntoLight'), url(https://fonts.gstatic.com/s/shadowsintolight/v7/clhLqOv7MXn459PTh0gXYFK2TSYBz0eNcHnp4YqE4Ts.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}
@font-face {
font-family: 'Uncial Antiqua';
font-style: normal;
font-weight: 400;
src: local('Uncial Antiqua'), local('UncialAntiqua-Regular'), url(https://fonts.gstatic.com/s/uncialantiqua/v5/N0bM2S5WOex4OUbESzoESK-i-MfWQZQ.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Underdog';
font-style: normal;
font-weight: 400;
src: local('Underdog'), local('Underdog-Regular'), url(https://fonts.gstatic.com/s/underdog/v6/CHygV-jCElj7diMroWSlWV8.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Yellowtail';
font-style: normal;
font-weight: 400;
src: local('Yellowtail Regular'), local('Yellowtail-Regular'), url(https://fonts.gstatic.com/s/yellowtail/v8/GcIHC9QEwVkrA19LJU1qlPk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
}

147
index.css

File diff suppressed because one or more lines are too long

View file

@ -242,7 +242,7 @@
<div id="collapsible"> <div id="collapsible">
<button id="optionsTrigger" data-t="tipOptionsTrigger" data-tip="Click to show options pane. Shortcut: Tab" class="options glow" onclick="showOptions(event)" style="padding:.6em .45em"></button> <button id="optionsTrigger" data-t="tipOptionsTrigger" data-tip="Click to show options pane. 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; padding:.6em 1em"><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()" 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">
@ -273,8 +273,8 @@
<option value="landmass">Pure landmass</option> <option value="landmass">Pure landmass</option>
<option hidden value="custom">Custom (not saved)</option> <option hidden value="custom">Custom (not saved)</option>
</select> </select>
<button id="savePresetButton" data-tip="Click to save displayed layers as a new preset" class="icon-plus presetButton" style="display:none" onclick="savePreset()"></button> <button id="savePresetButton" data-tip="Click to save displayed layers as a new preset" class="icon-plus sideButton" style="display:none" onclick="savePreset()"></button>
<button id="removePresetButton" data-tip="Click to remove current custom preset" class="icon-minus presetButton" style="display:none" onclick="removePreset()"></button> <button id="removePresetButton" data-tip="Click to remove current custom preset" class="icon-minus sideButton" style="display:none" onclick="removePreset()"></button>
<p data-tip="Click to toggle a layer, drag to raise or lower a layer. Ctrl + click to edit layer style">Displayed layers and layers order:</p> <p data-tip="Click to toggle a layer, drag to raise or lower a layer. Ctrl + click to edit layer style">Displayed layers and layers order:</p>
<ul data-tip="Click to toggle a layer, drag to raise or lower a layer. Ctrl + click to edit layer style" id="mapLayers"> <ul data-tip="Click to toggle a layer, drag to raise or lower a layer. Ctrl + click to edit layer style" id="mapLayers">
@ -326,8 +326,8 @@
<option value="styleWatercolor" data-system=1>Watercolor</option> <option value="styleWatercolor" data-system=1>Watercolor</option>
<option value="styleMonochrome" data-system=1>Monochrome</option> <option value="styleMonochrome" data-system=1>Monochrome</option>
</select> </select>
<button id="addStyleButton" data-tip="Click to save current style as a new preset" class="icon-plus styleButton" style="display: inline-block" onclick="addStylePreset()"></button> <button id="addStyleButton" data-tip="Click to save current style as a new preset" class="icon-plus sideButton" style="display: inline-block" onclick="addStylePreset()"></button>
<button id="removeStyleButton" data-tip="Click to remove current custom style preset" class="icon-minus styleButton" style="display: none" onclick="removeStylePreset()"></button> <button id="removeStyleButton" data-tip="Click to remove current custom style preset" class="icon-minus sideButton" style="display: none" onclick="removeStylePreset()"></button>
<p data-tip="Select an element to edit its style" style="display: inline-block;">Select element:</p> <p data-tip="Select an element to edit its style" style="display: inline-block;">Select element:</p>
<select data-tip="Select an element to edit its style (list is ordered alphabetically)" id="styleElementSelect" style="width:42%"> <select data-tip="Select an element to edit its style (list is ordered alphabetically)" id="styleElementSelect" style="width:42%">
@ -458,7 +458,7 @@
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/iran-small.jpg">Iran small</option> <option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/iran-small.jpg">Iran small</option>
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/spain-small.jpg">Spain small</option> <option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/spain-small.jpg">Spain small</option>
</select> </select>
<button data-tip="Click and provide a URL to image to be set as a texture" class="icon-plus styleButton" onclick="textureProvideURL()"></button> <button data-tip="Click and provide a URL to image to be set as a texture" class="icon-plus sideButton" onclick="textureProvideURL()"></button>
</td> </td>
</tr> </tr>
@ -666,8 +666,7 @@
<td>Font</td> <td>Font</td>
<td> <td>
<select id="styleSelectFont"></select> <select id="styleSelectFont"></select>
<input id="styleInputFont" data-tip="Provide a link to @font-face declaration or type Google font name" type="text" placeholder="link to @font-face"> <button id="styleFontAdd" data-tip="Add a font" class="icon-plus sideButton"></button>
<button id="styleFontAdd" data-tip="Add custom font from the web" class="icon-plus styleButton"></button>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -1311,14 +1310,15 @@
</tr> --> </tr> -->
</table> </table>
<button id="configureWorld" data-tip="Click to open world configurator to setup map position on Globe and World climate" onclick="editWorld()">Configure World</button> <div>
<button id="optionsReset" data-tip="Click to restore default options (page will be reloaded)" onclick="restoreDefaultOptions()">Reset to defaults</button> <button id="configureWorld" data-tip="Click to open world configurator to setup map position on Globe and World climate" onclick="editWorld()">Configure World</button>
<button id="optionsReset" data-tip="Click to restore default options (page will be reloaded)" onclick="restoreDefaultOptions()">Reset to defaults</button>
</div>
</div> </div>
<div id="toolsContent" class="tabcontent"> <div id="toolsContent" class="tabcontent">
<p>Click to configure:</p>
<div> <div>
<p>Click to configure:</p>
<button id="editHeightmapButton" data-tip="Click to open Heightmap customization menu. Shortcut: Shift + H">Heightmap</button> <button id="editHeightmapButton" data-tip="Click to open Heightmap customization menu. Shortcut: Shift + H">Heightmap</button>
<button id="editBiomesButton" data-tip="Click to open Biomes Editor. Shortcut: Shift + B">Biomes</button> <button id="editBiomesButton" data-tip="Click to open Biomes Editor. Shortcut: Shift + B">Biomes</button>
<button id="editStatesButton" data-tip="Click to open States Editor. Shortcut: Shift + S">States</button> <button id="editStatesButton" data-tip="Click to open States Editor. Shortcut: Shift + S">States</button>
@ -1333,18 +1333,18 @@
<button id="editNotesButton" data-tip="Click to open Notes Editor. Shortcut: Shift + O">Notes</button> <button id="editNotesButton" data-tip="Click to open Notes Editor. Shortcut: Shift + O">Notes</button>
</div> </div>
<p>Click to overview:</p>
<div> <div>
<p>Click to overview:</p>
<button id="overviewBurgsButton" data-tip="Click to open Burgs Overview. Shortcut: Shift + T">Burgs</button> <button id="overviewBurgsButton" data-tip="Click to open Burgs Overview. Shortcut: Shift + T">Burgs</button>
<button id="overviewRiversButton" data-tip="Click to open Rivers Overview. Shortcut: Shift + V">Rivers</button> <button id="overviewRiversButton" data-tip="Click to open Rivers Overview. Shortcut: Shift + V">Rivers</button>
<button id="overviewMilitaryButton" data-tip="Click to open Military Forces Overview. Shortcut: Shift + M">Military</button> <button id="overviewMilitaryButton" data-tip="Click to open Military Forces Overview. Shortcut: Shift + M">Military</button>
<button id="overviewCellsButton" data-tip="Click to open Cell details view. Shortcut: Shift + E">Cells</button> <button id="overviewCellsButton" data-tip="Click to open Cell details view. Shortcut: Shift + E">Cells</button>
</div> </div>
<p>Click to regenerate:</p>
<div id="regenerateFeature"> <div id="regenerateFeature">
<p>Click to regenerate:</p> <button id="regenerateStateLabels" data-tip="Click to update state labels placement based on current borders">Labels</button>
<button id="regenerateStateLabels" data-tip="Click to update state labels placement based on current borders">State labels</button> <button id="regenerateReliefIcons" data-tip="Click to regenerate all relief icons based on current cell biome and elevation">Relief</button>
<button id="regenerateReliefIcons" data-tip="Click to regenerate all relief icons based on current cell biome and elevation">Relief icons</button>
<button id="regenerateRoutes" data-tip="Click to regenerate all routes">Routes</button> <button id="regenerateRoutes" data-tip="Click to regenerate all routes">Routes</button>
<button id="regenerateRivers" data-tip="Click to regenerate all rivers (restore default state)">Rivers</button> <button id="regenerateRivers" data-tip="Click to regenerate all rivers (restore default state)">Rivers</button>
<button id="regeneratePopulation" data-tip="Click to recalculate rural and urban population">Population</button> <button id="regeneratePopulation" data-tip="Click to recalculate rural and urban population">Population</button>
@ -1360,8 +1360,8 @@
<button id="regenerateZones" data-tip="Click to regenerate zones. Hold Ctrl and click to set zones number multiplier">Zones</button> <button id="regenerateZones" data-tip="Click to regenerate zones. Hold Ctrl and click to set zones number multiplier">Zones</button>
</div> </div>
<p>Click to add:</p>
<div id="addFeature"> <div id="addFeature">
<p>Click to add:</p>
<button id="addBurgTool" data-tip="Click on map to place a burg. Hold <kbd>Shift</kbd> to add multiple. Shortcut: Shift + 1">Burg</button> <button id="addBurgTool" data-tip="Click on map to place a burg. Hold <kbd>Shift</kbd> to add multiple. Shortcut: Shift + 1">Burg</button>
<button id="addLabel" data-tip="Click on map to place label. Hold Shift to add multiple. Shortcut: Shift + 2">Label</button> <button id="addLabel" data-tip="Click on map to place label. Hold Shift to add multiple. Shortcut: Shift + 2">Label</button>
<button id="addRiver" data-tip="Click on map to place a river. Hold Shift to add multiple. Shortcut: Shift + 3">River</button> <button id="addRiver" data-tip="Click on map to place a river. Hold Shift to add multiple. Shortcut: Shift + 3">River</button>
@ -1385,8 +1385,8 @@
<button data-tip="Preview heightmap in 3D scene" id="heightmap3DView">3D scene</button> <button data-tip="Preview heightmap in 3D scene" id="heightmap3DView">3D scene</button>
</div> </div>
<p>Options:</p>
<div id="customizeOptions"> <div id="customizeOptions">
<p>Options:</p>
<div data-tip="Heightmap edit mode">Edit mode: <span id="heightmapEditMode"></span></div> <div data-tip="Heightmap edit mode">Edit mode: <span id="heightmapEditMode"></span></div>
<div data-tip="Render cells below the sea level (with height less than 20)"> <div data-tip="Render cells below the sea level (with height less than 20)">
<input id="renderOcean" class="checkbox" type="checkbox"> <input id="renderOcean" class="checkbox" type="checkbox">
@ -1409,14 +1409,14 @@
</div> </div>
</div> </div>
<p>Statistics:</p>
<div> <div>
<p>Statistics:</p>
<span>Land cells: </span><span id="landmassCounter">0</span> <span>Land cells: </span><span id="landmassCounter">0</span>
<span style="margin-left:.9em">Mean height: </span><span id="landmassAverage">0</span> <span style="margin-left:.9em">Mean height: </span><span id="landmassAverage">0</span>
</div> </div>
<p>Cell info:</p>
<div> <div>
<p>Cell info:</p>
<span>Coord: </span><span id="heightmapInfoX"></span>/<span id="heightmapInfoY"></span><br> <span>Coord: </span><span id="heightmapInfoX"></span>/<span id="heightmapInfoY"></span><br>
<span>Cell: </span><span id="heightmapInfoCell"></span><br> <span>Cell: </span><span id="heightmapInfoCell"></span><br>
<span>Height: </span><span id="heightmapInfoHeight"></span> <span>Height: </span><span id="heightmapInfoHeight"></span>
@ -3351,6 +3351,22 @@
</div> </div>
</div> </div>
<div id="addFontDialog" style="display: none" class="dialog">
<span>There are 3 ways to add a custom font:</span>
<p><strong>Google font</strong>. Open <a href="https://fonts.google.com/" target="_blank">Google Fonts</a>, find a font you like and enter its name to the field below.</p>
<p><strong>Local font</strong>. If you have a font <a href="https://faqs.skillcrush.com/article/275-downloading-installing-a-font-on-your-computer" target="_blank">installed on your computer</a>, just provide the font name. Make sure the browser is reloaded after the installation. The font won't work on machines not having it installed. Good source of fonts are <a href="https://fontesk.com" target="_blank">Fontdesk</a> and <a href="https://www.dafont.com" target="_blank">DaFont</a>.</p>
<p><strong>Font URL</strong>. Provide font name and link to the font file hosted online. The best free font hostings are <a href="https://fonts.google.com/" target="_blank">Google Fonts</a> and <a target="_blank" href="https://www.cdnfonts.com">CDN Fonts</a>. To get font file open the link to css provided by these services and manually copy the link to <code>woff2</code>.</p>
<div style="margin-top: .3em" data-tip="Select font adding method">
<select id="addFontMethod">
<option value="googleFont" selected>Google font</option>
<option value="localFont">Local font</option>
<option value="fontURL" selected>Font URL</option>
</select>
<input id="addFontNameInput" placeholder="font family" style="width:15em">
<input id="addFontURLInput" placeholder="font file URL" style="width:22.6em; margin-top:.1em">
</div>
</div>
<div id="cellInfo" style="display: none" class="dialog stable"> <div id="cellInfo" style="display: none" class="dialog stable">
<p><b>Cell:</b> <span id="infoCell"></span> <b>X:</b> <span id="infoX"></span> <b>Y:</b> <span id="infoY"></span></p> <p><b>Cell:</b> <span id="infoCell"></span> <b>X:</b> <span id="infoX"></span> <b>Y:</b> <span id="infoY"></span></p>
<p><b>Latitude:</b> <span id="infoLat"></span></p> <p><b>Latitude:</b> <span id="infoLat"></span></p>
@ -3447,7 +3463,7 @@
<div id="options3dBottom" style="margin-top: .2em"> <div id="options3dBottom" style="margin-top: .2em">
<button id="options3dUpdate" data-tip="Update the scene" class="icon-cw"></button> <button id="options3dUpdate" data-tip="Update the scene" class="icon-cw"></button>
<button data-tip="Configure world and map size and climate settings" onclick="editWorld()" class="icon-globe"></button> <button data-tip="Configure world and map size and climate settings" onclick="editWorld()" class="icon-globe"></button>
<button id="options3dSave" data-tip="Save screenshot of the 3d scene" class="icon-button-screenshot"></button> <button id="options3dSave" data-tip="Save screenshot of the 3d scene" class="icon-button-screenshot"></button>
<button id="options3dOBJSave" data-tip="Save OBJ file of the 3d scene" class="icon-download"></button> <button id="options3dOBJSave" data-tip="Save OBJ file of the 3d scene" class="icon-download"></button>
</div> </div>
</div> </div>
@ -3480,11 +3496,9 @@
<button onclick="saveGeoJSON_Rivers()" data-tip="Download rivers data in GeoJSON format">rivers</button> <button onclick="saveGeoJSON_Rivers()" data-tip="Download rivers data in GeoJSON format">rivers</button>
<button onclick="saveGeoJSON_Markers()" data-tip="Download markers data in GeoJSON format">markers</button> <button onclick="saveGeoJSON_Markers()" data-tip="Download markers data in GeoJSON format">markers</button>
</div> </div>
<p style="font-style: italic">GeoJSON format is used in GIS tools such as QGIS. Check out <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export" target="_blank">wiki-page</a> for guidance</p> <p>GeoJSON format is used in GIS tools such as QGIS. Check out <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export" target="_blank">wiki-page</a> for guidance.</p>
<p>Generator uses pop-up window to download files. Please ensure your browser does not block popups.</p>
<p style="font-style: italic">Generator uses pop-up window to download files. Please ensure your browser does not block popups.</p> <p>It's also possible to export map to <i>Foundry VTT</i>, see <a href="https://github.com/Ethck/azgaar-foundry" target="_blank">the module.</a></p>
<p style="font-style: italic">It's also possible to export map to Foundry VTT, see <a href="https://github.com/Ethck/azgaar-foundry" target="_blank">the module.</a></p>
</div> </div>
<div id="saveMapData" style="display: none" class="dialog"> <div id="saveMapData" style="display: none" class="dialog">
@ -3492,24 +3506,26 @@
<strong>Save map to</strong> <strong>Save map to</strong>
<button onclick="dowloadMap()" data-tip="Download .map file to your local disk. Shortcut: Ctrl + S">machine</button> <button onclick="dowloadMap()" data-tip="Download .map file to your local disk. Shortcut: Ctrl + S">machine</button>
<button onclick="saveToDropbox()" data-tip="Save .map file to your Dropbox">dropbox</button> <button onclick="saveToDropbox()" data-tip="Save .map file to your Dropbox">dropbox</button>
<button onclick="quickSave()" data-tip="Save the project to browser storage (quick save). It can be unreliable. Shortcut: F6">browser</button> <button onclick="quickSave()" data-tip="Save the project to browser storage. It can be unreliable. Shortcut: F6">browser</button>
</div> </div>
<p style="font-style: italic">Maps are saved in <i>.map</i> format, that can be loaded back via 'Load' in menu. Please keep noted that we do not keep any data on our side. There is no way to restore the progress if .map file is lost. Please keep old .map files on your machine or cloud storage as backups.</p> <p>Maps are saved in <i>.map</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>.map</i> files on your machine or cloud storage as backups.</p>
</div> </div>
<div id="loadMapData" style="display: none" class="dialog"> <div id="loadMapData" style="display: none" class="dialog">
<div style="margin-bottom: .3em">Load map from</div>
<div> <div>
<strong>Load map from</strong>
<button onclick="mapToLoad.click()" data-tip="Load .map file from local disk">local disk</button> <button onclick="mapToLoad.click()" data-tip="Load .map file from local disk">local disk</button>
<button onclick="loadURL()" data-tip="Load .map file from URL (server should allow CORS)">URL</button> <button onclick="loadURL()" data-tip="Load .map file from URL (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">
<p>From your Dropbox account:</p> <p style="margin-bottom: .3em">From your Dropbox account</p>
<select style="margin-bottom:.3em"> <select id="loadFromDropboxSelect" style="width: 22em"></select>
</select>
<button onclick="loadFromDropbox()" data-tip="Load .map file from your Dropbox">Open</button> <div id="loadFromDropboxButtons" style="margin-bottom: .3em">
<button onclick="createSharableDropboxLink()" data-tip="Select .map file on dropbox and share a sharable link">Create link</button> <button onclick="loadFromDropbox()" data-tip="Load .map file from your Dropbox">Open</button>
<button onclick="createSharableDropboxLink()" data-tip="Select file and create a link to share with your friends">Create link</button>
</div>
<div style="margin-top: .3em"> <div style="margin-top: .3em">
<div id="sharableLinkContainer" style="display: none"> <div id="sharableLinkContainer" style="display: none">
@ -3521,7 +3537,7 @@
</div> </div>
<div id="saveTilesScreen" style="display: none" class="dialog"> <div id="saveTilesScreen" style="display: none" class="dialog">
<p style="font-style: italic">Map will be split into tiles and downloaded as a single zip file. Avoid saving to big images</p> <p>Map will be split into tiles and downloaded as a single zip file. Avoid saving to big images</p>
<div data-tip="Number of columns" style="margin-bottom: .3em"> <div data-tip="Number of columns" style="margin-bottom: .3em">
<div class="label">Columns:</div> <div class="label">Columns:</div>
<input id="tileColsInput" data-stored="tileCols" type="range" min=2 max=20 value=8 style="width: 11em"> <input id="tileColsInput" data-stored="tileCols" type="range" min=2 max=20 value=8 style="width: 11em">

2
libs/dropins.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -2,7 +2,7 @@
// https://github.com/Azgaar/Fantasy-Map-Generator // https://github.com/Azgaar/Fantasy-Map-Generator
"use strict"; "use strict";
const version = "1.66"; // generator version1 const version = "1.661"; // generator version
document.title += " v" + version; document.title += " v" + version;
// Switches to disable/enable logging features // Switches to disable/enable logging features
@ -122,7 +122,6 @@ let customization = 0; // 0 - no; 1 = heightmap draw; 2 - states draw; 3 - add s
let biomesData = applyDefaultBiomesSystem(); let biomesData = applyDefaultBiomesSystem();
let nameBases = Names.getNameBases(); // cultures-related data let nameBases = Names.getNameBases(); // cultures-related data
const fonts = ["Almendra+SC", "Georgia", "Arial", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"]; // default fonts
let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme
const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation
@ -221,7 +220,7 @@ void (function checkLoadParameters() {
})(); })();
function generateMapOnLoad() { function generateMapOnLoad() {
applyStyleOnLoad(); // apply default of previously selected style applyStyleOnLoad(); // apply default or previously selected style
generate(); // generate map generate(); // generate map
focusOn(); // based on searchParams focus on point, cell or burg from MFCG focusOn(); // based on searchParams focus on point, cell or burg from MFCG
applyPreset(); // apply saved layers preset applyPreset(); // apply saved layers preset
@ -379,6 +378,7 @@ function showWelcomeMessage() {
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>. alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated. This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
<ul>Main changes: <ul>Main changes:
<li>Add custom fonts dialog</li>
<li>Save and load <i>.map</i> files to Dropbox</li> <li>Save and load <i>.map</i> files to Dropbox</li>
<li>Ability to add control points on river edit</li> <li>Ability to add control points on river edit</li>
<li>New heightmap template: Taklamakan</li> <li>New heightmap template: Taklamakan</li>

View file

@ -16,126 +16,124 @@ restore(): restore access tokens from storage if possible
*/ */
window.Cloud = (function () { window.Cloud = (function () {
// helpers to use in providers for token handling // helpers to use in providers for token handling
const lSKey = x => `auth-${x}` const lSKey = x => `auth-${x}`;
const setToken = (prov, key) => localStorage.setItem(lSKey(prov), key) const setToken = (prov, key) => localStorage.setItem(lSKey(prov), key);
const getToken = prov => localStorage.getItem(lSKey(prov)) const getToken = prov => localStorage.getItem(lSKey(prov));
/**********************************************************/ /**********************************************************/
/* Dropbox provider */ /* Dropbox provider */
/**********************************************************/ /**********************************************************/
const DBP = { const DBP = {
name: 'dropbox', name: "dropbox",
clientId: 'sp7tzwm27u2w5ns', clientId: "pdr9ae64ip0qno4",
authWindow: undefined, authWindow: undefined,
token: null, // Access token token: null, // Access token
api: null, api: null,
restore() { restore() {
this.token = getToken(this.name) this.token = getToken(this.name);
if (this.token) this.connect(this.token) if (this.token) this.connect(this.token);
}, },
async call(name, param) { async call(name, param) {
try { try {
return await this.api[name](param) return await this.api[name](param);
} catch (e) { } catch (e) {
if (e.name !== "DropboxResponseError") throw(e) if (e.name !== "DropboxResponseError") throw e;
// retry with auth // retry with auth
await this.auth() await this.auth();
return await this.api[name](param) return await this.api[name](param);
} }
}, },
connect(token) { connect(token) {
const clientId = this.clientId const clientId = this.clientId;
const auth = new Dropbox.DropboxAuth({ clientId }) const auth = new Dropbox.DropboxAuth({clientId});
auth.setAccessToken(token) auth.setAccessToken(token);
this.api = new Dropbox.Dropbox({ auth }) this.api = new Dropbox.Dropbox({auth});
}, },
async save(fileName, contents) { async save(fileName, contents) {
if (!this.api) await this.auth() if (!this.api) await this.auth();
const resp = this.call('filesUpload', { path: '/' + fileName, contents }) const resp = this.call("filesUpload", {path: "/" + fileName, contents});
console.log("Dropbox response:", resp) DEBUG && console.log("Dropbox response:", resp);
return true return true;
}, },
async load(path) { async load(path) {
if (!this.api) await this.auth() if (!this.api) await this.auth();
const resp = await this.call('filesDownload', { path }) const resp = await this.call("filesDownload", {path});
const blob = resp.result.fileBlob const blob = resp.result.fileBlob;
if (!blob) throw(new Error('Invalid response from dropbox.')) if (!blob) throw new Error("Invalid response from dropbox.");
return blob return blob;
}, },
async list() { async list() {
if (!this.api) return null if (!this.api) return null;
const resp = await this.call('filesListFolder', { path: '' }) const resp = await this.call("filesListFolder", {path: ""});
return resp.result.entries.map(e => ({ name: e.name, path: e.path_lower })) return resp.result.entries.map(e => ({name: e.name, path: e.path_lower}));
}, },
auth() { auth() {
const url = window.location.origin + window.location.pathname + 'dropbox.html' const url = window.location.origin + window.location.pathname + "dropbox.html";
this.authWindow = window.open(url, 'auth', 'width=640,height=480') this.authWindow = window.open(url, "auth", "width=640,height=480");
// child window expected to call // child window expected to call
// window.opener.Cloud.providers.dropbox.setDropBoxToken (see below) // window.opener.Cloud.providers.dropbox.setDropBoxToken (see below)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const watchDog = () => { const watchDog = () => {
this.authWindow.close() this.authWindow.close();
reject(new Error("Timeout. No auth for dropbox.")) reject(new Error("Timeout. No auth for dropbox."));
} };
setTimeout(watchDog, 120*1000) setTimeout(watchDog, 120 * 1000);
window.addEventListener('dropboxauth', e => { window.addEventListener("dropboxauth", e => {
clearTimeout(watchDog) clearTimeout(watchDog);
resolve() resolve();
}) });
}) });
}, },
// Callback function for auth window. // Callback function for auth window.
setDropBoxToken(token) { setDropBoxToken(token) {
console.log('Access token got:', token) DEBUG && console.log("Access token:", token);
setToken(this.name, token) setToken(this.name, token);
this.connect(token) this.connect(token);
this.authWindow.close() this.authWindow.close();
window.dispatchEvent(new Event('dropboxauth')) window.dispatchEvent(new Event("dropboxauth"));
}, },
async getLink(path) { async getLink(path) {
if (!this.api) await this.auth() if (!this.api) await this.auth();
let resp let resp;
// already exists? // already exists?
resp = await this.call('sharingListSharedLinks', { path }) resp = await this.call("sharingListSharedLinks", {path});
if (resp.result.links.length) if (resp.result.links.length) return resp.result.links[0].url;
return resp.result.links[0].url
// create new // create new
resp = await this.call('sharingCreateSharedLinkWithSettings', { resp = await this.call("sharingCreateSharedLinkWithSettings", {
path, path,
settings: { settings: {
require_password: false, require_password: false,
audience: 'public', audience: "public",
access: 'viewer', access: "viewer",
requested_visibility: 'public', requested_visibility: "public",
allow_download: true, allow_download: true
} }
}) });
console.log("dropbox link object:", resp.result) DEBUG && console.log("Dropbox link object:", resp.result);
return resp.result.url return resp.result.url;
}, }
} };
// register providers here: // register providers here:
const providers = { const providers = {
dropbox: DBP, dropbox: DBP
} };
// restore all providers at startup // restore all providers at startup
for (const p of Object.values(providers)) p.restore() for (const p of Object.values(providers)) p.restore();
return { providers } return {providers};
})() })();

View file

@ -1,141 +1,271 @@
// helper finctions to work with fonts "use strict";
async function addFonts(url) { const fonts = [
$("head").append('<link rel="stylesheet" type="text/css" href="' + url + '">'); {family: "Arial"},
{family: "Times New Roman"},
{family: "Georgia"},
{family: "Garamond"},
{family: "Lucida Sans Unicode"},
{family: "Courier New"},
{family: "Verdana"},
{family: "Impact"},
{family: "Comic Sans MS"},
{family: "Papyrus"},
{
family: "Almendra SC",
src: "url(https://fonts.gstatic.com/s/almendrasc/v13/Iure6Yx284eebowr7hbyTaZOrLQ.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Amatic SC",
src: "url(https://fonts.gstatic.com/s/amaticsc/v11/TUZ3zwprpvBS1izr_vOMscGKfrUC.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Architects Daughter",
src: "url(https://fonts.gstatic.com/s/architectsdaughter/v8/RXTgOOQ9AAtaVOHxx0IUBM3t7GjCYufj5TXV5VnA2p8.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Bitter",
src: "url(https://fonts.gstatic.com/s/bitter/v12/zfs6I-5mjWQ3nxqccMoL2A.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Caesar Dressing",
src: "url(https://fonts.gstatic.com/s/caesardressing/v6/yYLx0hLa3vawqtwdswbotmK4vrRHdrz7.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Cinzel",
src: "url(https://fonts.gstatic.com/s/cinzel/v7/zOdksD_UUTk1LJF9z4tURA.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Dancing Script",
src: "url(https://fonts.gstatic.com/s/dancingscript/v9/KGBfwabt0ZRLA5W1ywjowUHdOuSHeh0r6jGTOGdAKHA.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Fredericka the Great",
src: "url(https://fonts.gstatic.com/s/frederickathegreat/v6/9Bt33CxNwt7aOctW2xjbCstzwVKsIBVV--Sjxbc.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Gloria Hallelujah",
src: "url(https://fonts.gstatic.com/s/gloriahallelujah/v9/CA1k7SlXcY5kvI81M_R28cNDay8z-hHR7F16xrcXsJw.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Great Vibes",
src: "url(https://fonts.gstatic.com/s/greatvibes/v5/6q1c0ofG6NKsEhAc2eh-3Y4P5ICox8Kq3LLUNMylGO4.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "IM Fell English",
src: "url(https://fonts.gstatic.com/s/imfellenglish/v7/xwIisCqGFi8pff-oa9uSVAkYLEKE0CJQa8tfZYc_plY.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Kaushan Script",
src: "url(https://fonts.gstatic.com/s/kaushanscript/v6/qx1LSqts-NtiKcLw4N03IEd0sm1ffa_JvZxsF_BEwQk.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "MedievalSharp",
src: "url(https://fonts.gstatic.com/s/medievalsharp/v9/EvOJzAlL3oU5AQl2mP5KdgptMqhwMg.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Metamorphous",
src: "url(https://fonts.gstatic.com/s/metamorphous/v7/Wnz8HA03aAXcC39ZEX5y133EOyqs.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Montez",
src: "url(https://fonts.gstatic.com/s/montez/v8/aq8el3-0osHIcFK6bXAPkw.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Nova Script",
src: "url(https://fonts.gstatic.com/s/novascript/v10/7Au7p_IpkSWSTWaFWkumvlQKGFw.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Orbitron",
src: "url(https://fonts.gstatic.com/s/orbitron/v9/HmnHiRzvcnQr8CjBje6GQvesZW2xOQ-xsNqO47m55DA.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Satisfy",
src: "url(https://fonts.gstatic.com/s/satisfy/v8/2OzALGYfHwQjkPYWELy-cw.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Shadows Into Light",
src: "url(https://fonts.gstatic.com/s/shadowsintolight/v7/clhLqOv7MXn459PTh0gXYFK2TSYBz0eNcHnp4YqE4Ts.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
},
{
family: "Uncial Antiqua",
src: "url(https://fonts.gstatic.com/s/uncialantiqua/v5/N0bM2S5WOex4OUbESzoESK-i-MfWQZQ.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Underdog",
src: "url(https://fonts.gstatic.com/s/underdog/v6/CHygV-jCElj7diMroWSlWV8.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Yellowtail",
src: "url(https://fonts.gstatic.com/s/yellowtail/v8/GcIHC9QEwVkrA19LJU1qlPk_vArhqVIZ0nv9q090hN8.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215"
}
];
declareDefaultFonts(); // execute once on load
function declareFont(font) {
const {family, src, ...rest} = font;
if (!src) return;
const fontFace = new FontFace(family, src, {...rest, display: "block"});
document.fonts.add(fontFace);
addFontOption(family);
}
function declareDefaultFonts() {
fonts.forEach(font => {
if (font.src) declareFont(font);
else addFontOption(font.family);
});
}
function getUsedFonts(svg) {
const usedFontFamilies = new Set();
const labelGroups = svg.querySelectorAll("#labels g");
for (const labelGroup of labelGroups) {
const font = labelGroup.getAttribute("font-family");
if (font) usedFontFamilies.add(font);
}
const provinceFont = provs.attr("font-family");
if (provinceFont) usedFontFamilies.add(provinceFont);
const legend = svg.querySelector("#legend");
const legendFont = legend?.getAttribute("font-family");
if (legendFont) usedFontFamilies.add(legendFont);
const usedFonts = fonts.filter(font => usedFontFamilies.has(font.family));
return usedFonts;
}
function addFontOption(family) {
const options = document.getElementById("styleSelectFont");
// const existingOption = options.querySelector(`[value="${family}"]`);
// if (existingOption) return;
const option = document.createElement("option");
option.value = family;
option.innerText = family;
option.style.fontFamily = family;
options.add(option);
}
async function fetchGoogleFont(family) {
const url = `https://fonts.googleapis.com/css2?family=${family.replace(/ /g, "+")}`;
try { try {
const resp = await fetch(url); const resp = await fetch(url);
const text = await resp.text(); const text = await resp.text();
let s = document.createElement("style");
s.innerHTML = text; const fontFaceRules = text.match(/font-face\s*{[^}]+}/g);
document.head.appendChild(s); const fonts = fontFaceRules.map(fontFace => {
let styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0]; const srcURL = fontFace.match(/url\(['"]?(.+?)['"]?\)/)[1];
let FontRule = rule_1 => { const src = `url(${srcURL})`;
let family = rule_1.style.getPropertyValue("font-family"); const unicodeRange = fontFace.match(/unicode-range: (.*?);/)?.[1];
let font = family.replace(/['"]+/g, "").replace(/ /g, "+"); const variant = fontFace.match(/font-style: (.*?);/)?.[1];
let weight = rule_1.style.getPropertyValue("font-weight");
if (weight && weight !== "400") font += ":" + weight; const font = {family, src};
if (fonts.indexOf(font) == -1) { if (unicodeRange) font.unicodeRange = unicodeRange;
fonts.push(font); if (variant && variant !== "normal") font.variant = variant;
fetched++; return font;
} });
};
let fetched = 0; return fonts;
for (let r of styleSheet.cssRules) {
FontRule(r);
}
document.head.removeChild(s);
return fetched;
} catch (err) { } catch (err) {
return ERROR && console.error(err); ERROR && console.error(err);
return null;
} }
} }
function loadUsedFonts() { function readBlobAsDataURL(blob) {
const fontsInUse = getFontsList(svg); return new Promise(function (resolve, reject) {
const fontsToLoad = fontsInUse.filter(font => !fonts.includes(font)); const reader = new FileReader();
if (fontsToLoad?.length) { reader.onloadend = () => resolve(reader.result);
const url = "https://fonts.googleapis.com/css?family=" + fontsToLoad.join("|"); reader.onerror = reject;
addFonts(url); reader.readAsDataURL(blob);
}
}
function getFontsList(svg) {
const fontsInUse = [];
svg.selectAll("#labels > g").each(function () {
if (!this.hasChildNodes()) return;
const font = this.dataset.font;
if (font) fontsInUse.push(font);
});
if (legend?.node()?.hasChildNodes()) fontsInUse.push(legend.attr("data-font"));
return [...new Set(fontsInUse)];
}
// code from Kaiido's answer https://stackoverflow.com/questions/42402584/how-to-use-google-fonts-in-canvas-when-drawing-dom-objects-in-svg
function GFontToDataURI(url) {
if (!url) return Promise.resolve();
return fetch(url) // first fecth the embed stylesheet page
.then(resp => resp.text()) // we only need the text of it
.then(text => {
let s = document.createElement("style");
s.innerHTML = text;
document.head.appendChild(s);
const styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0];
const FontRule = rule => {
const src = rule.style.getPropertyValue("src");
const url = src ? src.split("url(")[1].split(")")[0] : "";
return {rule, src, url: url.substring(url.length - 1, 1)};
};
const fontProms = [];
for (const r of styleSheet.cssRules) {
let fR = FontRule(r);
if (!fR.url) continue;
fontProms.push(
fetch(fR.url) // fetch the actual font-file (.woff)
.then(resp => resp.blob())
.then(blob => {
return new Promise(resolve => {
let f = new FileReader();
f.onload = e => resolve(f.result);
f.readAsDataURL(blob);
});
})
.then(dataURL => fR.rule.cssText.replace(fR.url, dataURL))
);
}
document.head.removeChild(s); // clean up
return Promise.all(fontProms); // wait for all this has been done
});
}
// fetch default fonts if not done before
function loadDefaultFonts() {
if (!$('link[href="fonts.css"]').length) {
$("head").append('<link rel="stylesheet" type="text/css" href="fonts.css">');
const fontsToAdd = ["Amatic+SC:700", "IM+Fell+English", "Great+Vibes", "MedievalSharp", "Metamorphous", "Nova+Script", "Uncial+Antiqua", "Underdog", "Caesar+Dressing", "Bitter", "Yellowtail", "Montez", "Shadows+Into+Light", "Fredericka+the+Great", "Orbitron", "Dancing+Script:700", "Architects+Daughter", "Kaushan+Script", "Gloria+Hallelujah", "Satisfy", "Comfortaa:700", "Cinzel"];
fontsToAdd.forEach(function (f) {
if (fonts.indexOf(f) === -1) fonts.push(f);
});
updateFontOptions();
}
}
function fetchFonts(url) {
return new Promise((resolve, reject) => {
if (url === "") return tip("Use a direct link to any @font-face declaration or just font name to fetch from Google Fonts");
if (url.indexOf("http") === -1) {
url = url.replace(url.charAt(0), url.charAt(0).toUpperCase()).split(" ").join("+");
url = "https://fonts.googleapis.com/css?family=" + url;
}
addFonts(url).then(fetched => {
if (fetched === undefined) return tip("Cannot fetch font for this value!", false, "error");
if (fetched === 0) return tip("Already in the fonts list!", false, "error");
updateFontOptions();
if (fetched === 1) {
tip("Font " + fonts[fonts.length - 1] + " is fetched");
} else if (fetched > 1) {
tip(fetched + " fonts are added to the list");
}
resolve(fetched);
});
}); });
} }
// Update font list for Label and Burg Editors async function loadFontsAsDataURI(fonts) {
function updateFontOptions() { const promises = fonts.map(async font => {
styleSelectFont.innerHTML = ""; const url = font.src.match(/url\(['"]?(.+?)['"]?\)/)[1];
for (let i = 0; i < fonts.length; i++) { const resp = await fetch(url);
const opt = document.createElement("option"); const blob = await resp.blob();
opt.value = i; const dataURL = await readBlobAsDataURL(blob);
const font = fonts[i].split(":")[0].replace(/\+/g, " ");
opt.style.fontFamily = opt.innerHTML = font; return {...font, src: `url('${dataURL}')`};
styleSelectFont.add(opt); });
}
return await Promise.all(promises);
}
async function addGoogleFont(family) {
const fontRanges = await fetchGoogleFont(family);
if (!fontRanges) return tip("Cannot fetch Google font for this value", true, "error", 4000);
tip(`Google font ${family} is loading...`, true, "warn", 4000);
const promises = fontRanges.map(range => {
const {src, unicodeRange, variant} = range;
const fontFace = new FontFace(family, src, {unicodeRange, variant, display: "block"});
return fontFace.load();
});
Promise.all(promises)
.then(fontFaces => {
fontFaces.forEach(fontFace => document.fonts.add(fontFace));
fonts.push(...fontRanges);
tip(`Google font ${family} is added to the list`, true, "success", 4000);
addFontOption(family);
document.getElementById("styleSelectFont").value = family;
changeFont();
})
.catch(err => {
tip(`Failed to load Google font ${family}`, true, "error", 4000);
console.error(err);
});
}
function addLocalFont(family) {
fonts.push({family});
const fontFace = new FontFace(family, `local(${family})`, {display: "block"});
document.fonts.add(fontFace);
tip(`Local font ${family} is added to the fonts list`, true, "success", 4000);
addFontOption(family);
document.getElementById("styleSelectFont").value = family;
changeFont();
}
function addWebFont(family, url) {
const src = `url('${url}')`;
fonts.push({family, src});
const fontFace = new FontFace(family, src, {display: "block"});
document.fonts.add(fontFace);
tip(`Font ${family} is added to the list`, true, "success", 4000);
addFontOption(family);
document.getElementById("styleSelectFont").value = family;
changeFont();
} }

View file

@ -12,10 +12,11 @@ function quickLoad() {
}); });
} }
async function loadFromDropbox(fileName) { async function loadFromDropbox() {
const map = document.querySelector("#loadFromDropbox select").value; const mapPath = document.getElementById("loadFromDropboxSelect")?.value;
console.log('loading dropbox map', map);
const blob = await Cloud.providers.dropbox.load(map); DEBUG && console.log("Loading map from Dropbox:", mapPath);
const blob = await Cloud.providers.dropbox.load(mapPath);
uploadMap(blob); uploadMap(blob);
} }
@ -23,18 +24,18 @@ async function createSharableDropboxLink() {
const mapFile = document.querySelector("#loadFromDropbox select").value; const mapFile = document.querySelector("#loadFromDropbox select").value;
const sharableLink = document.getElementById("sharableLink"); const sharableLink = document.getElementById("sharableLink");
const sharableLinkContainer = document.getElementById("sharableLinkContainer"); const sharableLinkContainer = document.getElementById("sharableLinkContainer");
let url let url;
try { try {
url = await Cloud.providers.dropbox.getLink(mapFile); url = await Cloud.providers.dropbox.getLink(mapFile);
} catch { } catch {
tip("Dropbox API error. Can not create link.", true, "error", 2000); tip("Dropbox API error. Can not create link.", true, "error", 2000);
return return;
} }
const fmg = window.location.href.split("?")[0]; const fmg = window.location.href.split("?")[0];
const reallink= `${fmg}?maplink=${url}`; const reallink = `${fmg}?maplink=${url}`;
// voodoo magic required by the yellow god of CORS // voodoo magic required by the yellow god of CORS
const link = reallink.replace('www.dropbox.com/s/', 'dl.dropboxusercontent.com/1/view/') const link = reallink.replace("www.dropbox.com/s/", "dl.dropboxusercontent.com/1/view/");
const shortLink = link.slice(0, 50) + "..."; const shortLink = link.slice(0, 50) + "...";
sharableLinkContainer.style.display = "block"; sharableLinkContainer.style.display = "block";
@ -236,6 +237,15 @@ function parseLoadedData(data) {
if (data[2]) mapCoordinates = JSON.parse(data[2]); if (data[2]) mapCoordinates = JSON.parse(data[2]);
if (data[4]) notes = JSON.parse(data[4]); if (data[4]) notes = JSON.parse(data[4]);
if (data[33]) rulers.fromString(data[33]); if (data[33]) rulers.fromString(data[33]);
if (data[34]) {
const usedFonts = JSON.parse(data[34]);
usedFonts.forEach(usedFont => {
const {family: usedFamily, unicodeRange: usedRange, variant: usedVariant} = usedFont;
const defaultFont = fonts.find(({family, unicodeRange, variant}) => family === usedFamily && unicodeRange === usedRange && variant === usedVariant);
if (!defaultFont) fonts.push(usedFont);
declareFont(usedFont);
});
}
const biomes = data[3].split("|"); const biomes = data[3].split("|");
biomesData = applyDefaultBiomesSystem(); biomesData = applyDefaultBiomesSystem();
@ -309,8 +319,6 @@ function parseLoadedData(data) {
burgLabels = labels.select("#burgLabels"); burgLabels = labels.select("#burgLabels");
})(); })();
loadUsedFonts();
void (function parseGridData() { void (function parseGridData() {
grid = JSON.parse(data[6]); grid = JSON.parse(data[6]);
calculateVoronoi(grid, grid.points); calculateVoronoi(grid, grid.points);
@ -423,7 +431,7 @@ 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("data-font", "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");

View file

@ -275,14 +275,22 @@ async function getMapURL(type, options = {}) {
}); });
} }
// load non-standard fonts // TODO: add dataURL for all used fonts
const usedFonts = getFontsList(clone); const usedFonts = getUsedFonts(cloneEl);
const webSafe = ["Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"]; const fontsToLoad = usedFonts.filter(font => font.src);
const fontsToLoad = usedFonts.filter(font => !webSafe.includes(font));
if (fontsToLoad.length) { if (fontsToLoad.length) {
const url = "https://fonts.googleapis.com/css?family=" + fontsToLoad.join("|"); const dataURLfonts = await loadFontsAsDataURI(fontsToLoad);
const fontStyle = await GFontToDataURI(url);
if (fontStyle) clone.select("defs").append("style").text(fontStyle.join("\n")); const fontFaces = dataURLfonts
.map(({family, src, unicodeRange = "", variant = "normal"}) => {
return `@font-face {font-family: "${family}"; src: ${src}; unicode-range: ${unicodeRange}; font-variant: ${variant};}`;
})
.join("\n");
const style = document.createElement("style");
style.setAttribute("type", "text/css");
style.innerHTML = fontFaces;
cloneEl.querySelector("defs").appendChild(style);
} }
clone.remove(); clone.remove();
@ -378,6 +386,7 @@ function getMapData() {
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
const notesData = JSON.stringify(notes); const notesData = JSON.stringify(notes);
const rulersString = rulers.toString(); const rulersString = rulers.toString();
const fonts = JSON.stringify(getUsedFonts(svg.node()));
// save svg // save svg
const cloneEl = document.getElementById("map").cloneNode(true); const cloneEl = document.getElementById("map").cloneNode(true);
@ -414,7 +423,7 @@ function getMapData() {
const pop = Array.from(pack.cells.pop).map(p => rn(p, 4)); const pop = Array.from(pack.cells.pop).map(p => rn(p, 4));
// data format as below // data format as below
const mapData = [params, settings, coords, biomes, notesData, serializedSVG, gridGeneral, grid.cells.h, grid.cells.prec, grid.cells.f, grid.cells.t, grid.cells.temp, packFeatures, cultures, states, burgs, pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl, pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state, pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces, namesData, rivers, rulersString].join("\r\n"); const mapData = [params, settings, coords, biomes, notesData, serializedSVG, gridGeneral, grid.cells.h, grid.cells.prec, grid.cells.f, grid.cells.t, grid.cells.temp, packFeatures, cultures, states, burgs, pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl, pop, pack.cells.r, pack.cells.road, pack.cells.s, pack.cells.state, pack.cells.religion, pack.cells.province, pack.cells.crossroad, religions, provinces, namesData, rivers, rulersString, fonts].join("\r\n");
TIME && console.timeEnd("createMapData"); TIME && console.timeEnd("createMapData");
return mapData; return mapData;
} }

View file

@ -32,15 +32,20 @@ function tip(tip = "Tip is undefined", main, type, time) {
else if (type === "warn") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)"; else if (type === "warn") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)";
else if (type === "success") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)"; else if (type === "success") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)";
if (main) tooltip.dataset.main = tip; // set main tip if (main) {
if (time) setTimeout(() => (tooltip.dataset.main = ""), time); // clear main in some time tooltip.dataset.main = tip;
tooltip.dataset.color = tooltip.style.background;
}
if (time) setTimeout(() => clearMainTip(), time);
} }
function showMainTip() { function showMainTip() {
tooltip.style.background = tooltip.dataset.color;
tooltip.innerHTML = tooltip.dataset.main; tooltip.innerHTML = tooltip.dataset.main;
} }
function clearMainTip() { function clearMainTip() {
tooltip.dataset.color = "";
tooltip.dataset.main = ""; tooltip.dataset.main = "";
tooltip.innerHTML = ""; tooltip.innerHTML = "";
} }

View file

@ -8,7 +8,7 @@ function editHeightmap() {
<p>You can <i>keep</i> the data, but you won't be able to change the coastline.</p> <p>You can <i>keep</i> the data, but you won't be able to change the coastline.</p>
<p>Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.</p> <p>Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.</p>
<p>Please <span class="pseudoLink" onclick=dowloadMap(); editHeightmap();>save the map</span> before editing the heightmap!</p> <p>Please <span class="pseudoLink" onclick=dowloadMap(); editHeightmap();>save the map</span> before editing the heightmap!</p>
<p>Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>`; <p style="margin-bottom: 0">Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>`;
$("#alert").dialog({ $("#alert").dialog({
resizable: false, resizable: false,

View file

@ -663,7 +663,7 @@ function showSavePane() {
$("#saveMapData").dialog({ $("#saveMapData").dialog({
title: "Save map", title: "Save map",
resizable: false, resizable: false,
width: "27em", width: "25em",
position: {my: "center", at: "center", of: "svg"}, position: {my: "center", at: "center", of: "svg"},
buttons: { buttons: {
Close: function () { Close: function () {
@ -699,7 +699,7 @@ async function showLoadPane() {
$("#loadMapData").dialog({ $("#loadMapData").dialog({
title: "Load map", title: "Load map",
resizable: false, resizable: false,
width: "22em", width: "24em",
position: {my: "center", at: "center", of: "svg"}, position: {my: "center", at: "center", of: "svg"},
buttons: { buttons: {
Close: function () { Close: function () {
@ -708,17 +708,23 @@ async function showLoadPane() {
} }
}); });
const dpx = document.getElementById("loadFromDropbox"); const loadFromDropboxButtons = document.getElementById("loadFromDropboxButtons");
const dpf = dpx.querySelector("select"); const fileSelect = document.getElementById("loadFromDropboxSelect");
const files = await Cloud.providers.dropbox.list(); const files = await Cloud.providers.dropbox.list();
dpx.style.display = files? "block" : "none";
if (!files) return; if (!files) {
while(dpf.firstChild) dpf.removeChild(dpf.firstChild); loadFromDropboxButtons.style.display = "none";
files.forEach(f => { fileSelect.innerHTML = `<option value="" disabled selected>Save files to Dropbox first</option>`;
const opt = document.createElement('option'); return;
opt.innerText = f.name; }
opt.value = f.path;
dpf.appendChild(opt); loadFromDropboxButtons.style.display = "block";
fileSelect.innerHTML = "";
files.forEach(file => {
const opt = document.createElement("option");
opt.innerText = file.name;
opt.value = file.path;
fileSelect.appendChild(opt);
}); });
} }

File diff suppressed because one or more lines are too long

View file

@ -475,7 +475,7 @@ function addLabelOnClick() {
const id = getNextId("label"); const id = getNextId("label");
let group = labels.select("#addedLabels"); let group = labels.select("#addedLabels");
if (!group.size()) group = labels.append("g").attr("id", "addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("data-font", "Almendra+SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null); if (!group.size()) group = labels.append("g").attr("id", "addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null);
const example = group.append("text").attr("x", 0).attr("x", 0).text(name); const example = group.append("text").attr("x", 0).attr("x", 0).text(name);
const width = example.node().getBBox().width; const width = example.node().getBBox().width;