mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-23 20:41:23 +01:00
Merge remote-tracking branch 'upstream/master' into dev-submap
This commit is contained in:
commit
1d1cd0bb71
15 changed files with 594 additions and 607 deletions
134
modules/cloud.js
134
modules/cloud.js
|
|
@ -16,126 +16,124 @@ restore(): restore access tokens from storage if possible
|
|||
*/
|
||||
|
||||
window.Cloud = (function () {
|
||||
|
||||
// helpers to use in providers for token handling
|
||||
const lSKey = x => `auth-${x}`
|
||||
const setToken = (prov, key) => localStorage.setItem(lSKey(prov), key)
|
||||
const getToken = prov => localStorage.getItem(lSKey(prov))
|
||||
const lSKey = x => `auth-${x}`;
|
||||
const setToken = (prov, key) => localStorage.setItem(lSKey(prov), key);
|
||||
const getToken = prov => localStorage.getItem(lSKey(prov));
|
||||
|
||||
/**********************************************************/
|
||||
/* Dropbox provider */
|
||||
/**********************************************************/
|
||||
|
||||
const DBP = {
|
||||
name: 'dropbox',
|
||||
clientId: 'sp7tzwm27u2w5ns',
|
||||
name: "dropbox",
|
||||
clientId: "pdr9ae64ip0qno4",
|
||||
authWindow: undefined,
|
||||
token: null, // Access token
|
||||
api: null,
|
||||
|
||||
restore() {
|
||||
this.token = getToken(this.name)
|
||||
if (this.token) this.connect(this.token)
|
||||
this.token = getToken(this.name);
|
||||
if (this.token) this.connect(this.token);
|
||||
},
|
||||
|
||||
async call(name, param) {
|
||||
try {
|
||||
return await this.api[name](param)
|
||||
return await this.api[name](param);
|
||||
} catch (e) {
|
||||
if (e.name !== "DropboxResponseError") throw(e)
|
||||
if (e.name !== "DropboxResponseError") throw e;
|
||||
// retry with auth
|
||||
await this.auth()
|
||||
return await this.api[name](param)
|
||||
await this.auth();
|
||||
return await this.api[name](param);
|
||||
}
|
||||
},
|
||||
|
||||
connect(token) {
|
||||
const clientId = this.clientId
|
||||
const auth = new Dropbox.DropboxAuth({ clientId })
|
||||
auth.setAccessToken(token)
|
||||
this.api = new Dropbox.Dropbox({ auth })
|
||||
const clientId = this.clientId;
|
||||
const auth = new Dropbox.DropboxAuth({clientId});
|
||||
auth.setAccessToken(token);
|
||||
this.api = new Dropbox.Dropbox({auth});
|
||||
},
|
||||
|
||||
async save(fileName, contents) {
|
||||
if (!this.api) await this.auth()
|
||||
const resp = this.call('filesUpload', { path: '/' + fileName, contents })
|
||||
console.log("Dropbox response:", resp)
|
||||
return true
|
||||
if (!this.api) await this.auth();
|
||||
const resp = this.call("filesUpload", {path: "/" + fileName, contents});
|
||||
DEBUG && console.log("Dropbox response:", resp);
|
||||
return true;
|
||||
},
|
||||
|
||||
async load(path) {
|
||||
if (!this.api) await this.auth()
|
||||
const resp = await this.call('filesDownload', { path })
|
||||
const blob = resp.result.fileBlob
|
||||
if (!blob) throw(new Error('Invalid response from dropbox.'))
|
||||
return blob
|
||||
if (!this.api) await this.auth();
|
||||
const resp = await this.call("filesDownload", {path});
|
||||
const blob = resp.result.fileBlob;
|
||||
if (!blob) throw new Error("Invalid response from dropbox.");
|
||||
return blob;
|
||||
},
|
||||
|
||||
async list() {
|
||||
if (!this.api) return null
|
||||
const resp = await this.call('filesListFolder', { path: '' })
|
||||
return resp.result.entries.map(e => ({ name: e.name, path: e.path_lower }))
|
||||
if (!this.api) return null;
|
||||
const resp = await this.call("filesListFolder", {path: ""});
|
||||
return resp.result.entries.map(e => ({name: e.name, path: e.path_lower}));
|
||||
},
|
||||
|
||||
auth() {
|
||||
const url = window.location.origin + window.location.pathname + 'dropbox.html'
|
||||
this.authWindow = window.open(url, 'auth', 'width=640,height=480')
|
||||
// child window expected to call
|
||||
// window.opener.Cloud.providers.dropbox.setDropBoxToken (see below)
|
||||
return new Promise((resolve, reject) => {
|
||||
const watchDog = () => {
|
||||
this.authWindow.close()
|
||||
reject(new Error("Timeout. No auth for dropbox."))
|
||||
}
|
||||
setTimeout(watchDog, 120*1000)
|
||||
window.addEventListener('dropboxauth', e => {
|
||||
clearTimeout(watchDog)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
const url = window.location.origin + window.location.pathname + "dropbox.html";
|
||||
this.authWindow = window.open(url, "auth", "width=640,height=480");
|
||||
// child window expected to call
|
||||
// window.opener.Cloud.providers.dropbox.setDropBoxToken (see below)
|
||||
return new Promise((resolve, reject) => {
|
||||
const watchDog = () => {
|
||||
this.authWindow.close();
|
||||
reject(new Error("Timeout. No auth for dropbox."));
|
||||
};
|
||||
setTimeout(watchDog, 120 * 1000);
|
||||
window.addEventListener("dropboxauth", e => {
|
||||
clearTimeout(watchDog);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// Callback function for auth window.
|
||||
setDropBoxToken(token) {
|
||||
console.log('Access token got:', token)
|
||||
setToken(this.name, token)
|
||||
this.connect(token)
|
||||
this.authWindow.close()
|
||||
window.dispatchEvent(new Event('dropboxauth'))
|
||||
DEBUG && console.log("Access token:", token);
|
||||
setToken(this.name, token);
|
||||
this.connect(token);
|
||||
this.authWindow.close();
|
||||
window.dispatchEvent(new Event("dropboxauth"));
|
||||
},
|
||||
|
||||
async getLink(path) {
|
||||
if (!this.api) await this.auth()
|
||||
let resp
|
||||
if (!this.api) await this.auth();
|
||||
let resp;
|
||||
|
||||
// already exists?
|
||||
resp = await this.call('sharingListSharedLinks', { path })
|
||||
if (resp.result.links.length)
|
||||
return resp.result.links[0].url
|
||||
resp = await this.call("sharingListSharedLinks", {path});
|
||||
if (resp.result.links.length) return resp.result.links[0].url;
|
||||
|
||||
// create new
|
||||
resp = await this.call('sharingCreateSharedLinkWithSettings', {
|
||||
resp = await this.call("sharingCreateSharedLinkWithSettings", {
|
||||
path,
|
||||
settings: {
|
||||
require_password: false,
|
||||
audience: 'public',
|
||||
access: 'viewer',
|
||||
requested_visibility: 'public',
|
||||
allow_download: true,
|
||||
audience: "public",
|
||||
access: "viewer",
|
||||
requested_visibility: "public",
|
||||
allow_download: true
|
||||
}
|
||||
})
|
||||
console.log("dropbox link object:", resp.result)
|
||||
return resp.result.url
|
||||
},
|
||||
}
|
||||
});
|
||||
DEBUG && console.log("Dropbox link object:", resp.result);
|
||||
return resp.result.url;
|
||||
}
|
||||
};
|
||||
|
||||
// register providers here:
|
||||
const providers = {
|
||||
dropbox: DBP,
|
||||
}
|
||||
dropbox: DBP
|
||||
};
|
||||
|
||||
// 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};
|
||||
})();
|
||||
|
|
|
|||
388
modules/fonts.js
388
modules/fonts.js
|
|
@ -1,141 +1,271 @@
|
|||
// helper finctions to work with fonts
|
||||
"use strict";
|
||||
|
||||
async function addFonts(url) {
|
||||
$("head").append('<link rel="stylesheet" type="text/css" href="' + url + '">');
|
||||
const fonts = [
|
||||
{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 {
|
||||
const resp = await fetch(url);
|
||||
const text = await resp.text();
|
||||
let s = document.createElement("style");
|
||||
s.innerHTML = text;
|
||||
document.head.appendChild(s);
|
||||
let styleSheet = Array.prototype.filter.call(document.styleSheets, sS => sS.ownerNode === s)[0];
|
||||
let FontRule = rule_1 => {
|
||||
let family = rule_1.style.getPropertyValue("font-family");
|
||||
let font = family.replace(/['"]+/g, "").replace(/ /g, "+");
|
||||
let weight = rule_1.style.getPropertyValue("font-weight");
|
||||
if (weight && weight !== "400") font += ":" + weight;
|
||||
if (fonts.indexOf(font) == -1) {
|
||||
fonts.push(font);
|
||||
fetched++;
|
||||
}
|
||||
};
|
||||
let fetched = 0;
|
||||
for (let r of styleSheet.cssRules) {
|
||||
FontRule(r);
|
||||
}
|
||||
document.head.removeChild(s);
|
||||
return fetched;
|
||||
|
||||
const fontFaceRules = text.match(/font-face\s*{[^}]+}/g);
|
||||
const fonts = fontFaceRules.map(fontFace => {
|
||||
const srcURL = fontFace.match(/url\(['"]?(.+?)['"]?\)/)[1];
|
||||
const src = `url(${srcURL})`;
|
||||
const unicodeRange = fontFace.match(/unicode-range: (.*?);/)?.[1];
|
||||
const variant = fontFace.match(/font-style: (.*?);/)?.[1];
|
||||
|
||||
const font = {family, src};
|
||||
if (unicodeRange) font.unicodeRange = unicodeRange;
|
||||
if (variant && variant !== "normal") font.variant = variant;
|
||||
return font;
|
||||
});
|
||||
|
||||
return fonts;
|
||||
} catch (err) {
|
||||
return ERROR && console.error(err);
|
||||
ERROR && console.error(err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function loadUsedFonts() {
|
||||
const fontsInUse = getFontsList(svg);
|
||||
const fontsToLoad = fontsInUse.filter(font => !fonts.includes(font));
|
||||
if (fontsToLoad?.length) {
|
||||
const url = "https://fonts.googleapis.com/css?family=" + fontsToLoad.join("|");
|
||||
addFonts(url);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
function readBlobAsDataURL(blob) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(blob);
|
||||
});
|
||||
}
|
||||
|
||||
// Update font list for Label and Burg Editors
|
||||
function updateFontOptions() {
|
||||
styleSelectFont.innerHTML = "";
|
||||
for (let i = 0; i < fonts.length; i++) {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = i;
|
||||
const font = fonts[i].split(":")[0].replace(/\+/g, " ");
|
||||
opt.style.fontFamily = opt.innerHTML = font;
|
||||
styleSelectFont.add(opt);
|
||||
}
|
||||
async function loadFontsAsDataURI(fonts) {
|
||||
const promises = fonts.map(async font => {
|
||||
const url = font.src.match(/url\(['"]?(.+?)['"]?\)/)[1];
|
||||
const resp = await fetch(url);
|
||||
const blob = await resp.blob();
|
||||
const dataURL = await readBlobAsDataURL(blob);
|
||||
|
||||
return {...font, src: `url('${dataURL}')`};
|
||||
});
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,11 @@ function quickLoad() {
|
|||
});
|
||||
}
|
||||
|
||||
async function loadFromDropbox(fileName) {
|
||||
const map = document.querySelector("#loadFromDropbox select").value;
|
||||
console.log('loading dropbox map', map);
|
||||
const blob = await Cloud.providers.dropbox.load(map);
|
||||
async function loadFromDropbox() {
|
||||
const mapPath = document.getElementById("loadFromDropboxSelect")?.value;
|
||||
|
||||
DEBUG && console.log("Loading map from Dropbox:", mapPath);
|
||||
const blob = await Cloud.providers.dropbox.load(mapPath);
|
||||
uploadMap(blob);
|
||||
}
|
||||
|
||||
|
|
@ -23,18 +24,18 @@ async function createSharableDropboxLink() {
|
|||
const mapFile = document.querySelector("#loadFromDropbox select").value;
|
||||
const sharableLink = document.getElementById("sharableLink");
|
||||
const sharableLinkContainer = document.getElementById("sharableLinkContainer");
|
||||
let url
|
||||
let url;
|
||||
try {
|
||||
url = await Cloud.providers.dropbox.getLink(mapFile);
|
||||
} catch {
|
||||
tip("Dropbox API error. Can not create link.", true, "error", 2000);
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
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) + "...";
|
||||
|
||||
sharableLinkContainer.style.display = "block";
|
||||
|
|
@ -236,6 +237,15 @@ function parseLoadedData(data) {
|
|||
if (data[2]) mapCoordinates = JSON.parse(data[2]);
|
||||
if (data[4]) notes = JSON.parse(data[4]);
|
||||
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("|");
|
||||
biomesData = applyDefaultBiomesSystem();
|
||||
|
|
@ -309,8 +319,6 @@ function parseLoadedData(data) {
|
|||
burgLabels = labels.select("#burgLabels");
|
||||
})();
|
||||
|
||||
loadUsedFonts();
|
||||
|
||||
void (function parseGridData() {
|
||||
grid = JSON.parse(data[6]);
|
||||
calculateVoronoi(grid, grid.points);
|
||||
|
|
@ -423,7 +431,7 @@ function parseLoadedData(data) {
|
|||
|
||||
// 1.0 adds a legend box
|
||||
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()
|
||||
stateBorders = borders.append("g").attr("id", "stateBorders");
|
||||
|
|
|
|||
|
|
@ -275,14 +275,22 @@ async function getMapURL(type, options = {}) {
|
|||
});
|
||||
}
|
||||
|
||||
// load non-standard fonts
|
||||
const usedFonts = getFontsList(clone);
|
||||
const webSafe = ["Georgia", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New", "Verdana", "Arial", "Impact"];
|
||||
const fontsToLoad = usedFonts.filter(font => !webSafe.includes(font));
|
||||
// TODO: add dataURL for all used fonts
|
||||
const usedFonts = getUsedFonts(cloneEl);
|
||||
const fontsToLoad = usedFonts.filter(font => font.src);
|
||||
if (fontsToLoad.length) {
|
||||
const url = "https://fonts.googleapis.com/css?family=" + fontsToLoad.join("|");
|
||||
const fontStyle = await GFontToDataURI(url);
|
||||
if (fontStyle) clone.select("defs").append("style").text(fontStyle.join("\n"));
|
||||
const dataURLfonts = await loadFontsAsDataURI(fontsToLoad);
|
||||
|
||||
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();
|
||||
|
|
@ -378,6 +386,7 @@ function getMapData() {
|
|||
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
|
||||
const notesData = JSON.stringify(notes);
|
||||
const rulersString = rulers.toString();
|
||||
const fonts = JSON.stringify(getUsedFonts(svg.node()));
|
||||
|
||||
// save svg
|
||||
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));
|
||||
|
||||
// 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");
|
||||
return mapData;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 === "success") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)";
|
||||
|
||||
if (main) tooltip.dataset.main = tip; // set main tip
|
||||
if (time) setTimeout(() => (tooltip.dataset.main = ""), time); // clear main in some time
|
||||
if (main) {
|
||||
tooltip.dataset.main = tip;
|
||||
tooltip.dataset.color = tooltip.style.background;
|
||||
}
|
||||
if (time) setTimeout(() => clearMainTip(), time);
|
||||
}
|
||||
|
||||
function showMainTip() {
|
||||
tooltip.style.background = tooltip.dataset.color;
|
||||
tooltip.innerHTML = tooltip.dataset.main;
|
||||
}
|
||||
|
||||
function clearMainTip() {
|
||||
tooltip.dataset.color = "";
|
||||
tooltip.dataset.main = "";
|
||||
tooltip.innerHTML = "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>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>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({
|
||||
resizable: false,
|
||||
|
|
|
|||
|
|
@ -663,7 +663,7 @@ function showSavePane() {
|
|||
$("#saveMapData").dialog({
|
||||
title: "Save map",
|
||||
resizable: false,
|
||||
width: "27em",
|
||||
width: "25em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Close: function () {
|
||||
|
|
@ -699,7 +699,7 @@ async function showLoadPane() {
|
|||
$("#loadMapData").dialog({
|
||||
title: "Load map",
|
||||
resizable: false,
|
||||
width: "22em",
|
||||
width: "24em",
|
||||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Close: function () {
|
||||
|
|
@ -708,17 +708,23 @@ async function showLoadPane() {
|
|||
}
|
||||
});
|
||||
|
||||
const dpx = document.getElementById("loadFromDropbox");
|
||||
const dpf = dpx.querySelector("select");
|
||||
const loadFromDropboxButtons = document.getElementById("loadFromDropboxButtons");
|
||||
const fileSelect = document.getElementById("loadFromDropboxSelect");
|
||||
const files = await Cloud.providers.dropbox.list();
|
||||
dpx.style.display = files? "block" : "none";
|
||||
if (!files) return;
|
||||
while(dpf.firstChild) dpf.removeChild(dpf.firstChild);
|
||||
files.forEach(f => {
|
||||
const opt = document.createElement('option');
|
||||
opt.innerText = f.name;
|
||||
opt.value = f.path;
|
||||
dpf.appendChild(opt);
|
||||
|
||||
if (!files) {
|
||||
loadFromDropboxButtons.style.display = "none";
|
||||
fileSelect.innerHTML = `<option value="" disabled selected>Save files to Dropbox first</option>`;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
|
@ -475,7 +475,7 @@ function addLabelOnClick() {
|
|||
const id = getNextId("label");
|
||||
|
||||
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 width = example.node().getBBox().width;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue