diff --git a/index.css b/index.css
index 89cf0fb6..149db0b6 100644
--- a/index.css
+++ b/index.css
@@ -1581,9 +1581,18 @@ rect.fillRect {
width: 9em;
}
-
#emblemsBottom {
margin-top: 4px;
+ text-align: center;
+}
+
+#emblemDownloadControl {
+ margin-top: .3em;
+ text-align: center;
+}
+
+#emblemDownloadControl > input {
+ width: 4.1em;
}
#picker text {
diff --git a/index.html b/index.html
index 87b3f7cc..334ae80e 100644
--- a/index.html
+++ b/index.html
@@ -3343,6 +3343,7 @@
diff --git a/modules/coa-generator.js b/modules/coa-generator.js
index d0b599f4..71f119d4 100644
--- a/modules/coa-generator.js
+++ b/modules/coa-generator.js
@@ -209,6 +209,8 @@
// test in FF
// generate all?
// layout preset
+ // burg editor - add emblem
+ // other editors
const t1 = P(kinship) ? parent.t1 : getTincture("field");
const coa = {t1};
diff --git a/modules/coa-renderer.js b/modules/coa-renderer.js
index 3f9fbadb..6318a98a 100644
--- a/modules/coa-renderer.js
+++ b/modules/coa-renderer.js
@@ -672,7 +672,7 @@
moriaOrc: "0 0 200 200"
}
- async function draw(id, coa) {
+ function draw(id, coa) {
const {division, ordinaries = [], charges = []} = coa;
const ordinariesRegular = ordinaries.filter(o => !o.above);
const ordinariesAboveCharges = ordinaries.filter(o => o.above);
@@ -877,7 +877,7 @@
}
// async render coa if it does not exist
- const trigger = async function(id, coa) {
+ const trigger = function(id, coa) {
if (!document.getElementById(id)) draw(id, coa);
}
diff --git a/modules/ui/emblems-editor.js b/modules/ui/emblems-editor.js
index 11aa9e80..43416eeb 100644
--- a/modules/ui/emblems-editor.js
+++ b/modules/ui/emblems-editor.js
@@ -25,7 +25,11 @@ function editEmblem(type, id, el) {
document.getElementById("emblemShapeSelector").oninput = changeShape;
document.getElementById("emblemsRegenerate").onclick = regenerate;
document.getElementById("emblemsArmoria").onclick = openInArmoria;
- document.getElementById("emblemsDownload").onclick = download;
+ document.getElementById("emblemsDownload").onclick = toggleDownload;
+ document.getElementById("emblemsDownloadSVG").onclick = () => download("svg");
+ document.getElementById("emblemsDownloadPNG").onclick = () => download("png");
+ document.getElementById("emblemsDownloadJPG").onclick = () => download("jpeg");
+ document.getElementById("emblemsGallery").onclick = downloadGallery;
document.getElementById("emblemsFocus").onclick = showArea;
function defineEmblemData(e) {
@@ -164,57 +168,167 @@ function editEmblem(type, id, el) {
openURL(url);
}
- function download() {
+ function toggleDownload() {
+ const buttons = document.getElementById("emblemDownloadControl");
+ buttons.classList.toggle("hidden");
+ }
+
+ function download(format) {
const coa = document.getElementById(id);
+ const size = +emblemsDownloadSize.value;
+ const url = getURL(coa, el.coa, size);
+ const link = document.createElement("a");
+ link.download = getFileName(`Emblem ${el.fullName || el.name}`) + "." + format;
+
+ if (format === "svg") downloadSVG(url, link); else downloadRaster(format, url, link, size);
+ document.getElementById("emblemDownloadControl").classList.add("hidden");
+ }
+
+ function downloadSVG(url, link) {
+ link.href = url;
+ document.body.appendChild(link);
+ link.click();
+ window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
+ }
+
+ function downloadRaster(format, url, link, size) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
- canvas.width = 500;
- canvas.height = 500;
+ canvas.width = size;
+ canvas.height = size;
- const url = getURL(coa, el.coa);
const img = new Image();
img.src = url;
- img.onload = () => {
- URL.revokeObjectURL(url);
+ img.onload = async function() {
+ if (format === "jpeg") {
+ ctx.fillStyle = "#fff";
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
- drawCanvas(canvas, el);
- }
-
- function drawCanvas(canvas, el) {
- const link = document.createElement("a");
- link.download = getFileName(`Emblem ${el.fullName || el.name}`) + ".png";
- canvas.toBlob(function (blob) {
- link.href = window.URL.createObjectURL(blob);
- document.body.appendChild(link);
- link.click();
- setTimeout(function () {
- canvas.remove();
- window.URL.revokeObjectURL(link.href);
- }, 5000);
- });
+ const URL = await canvas.toDataURL("image/" + format, .92);
+ link.href = URL;
+ document.body.appendChild(link);
+ link.click();
+ window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
}
}
- function getURL(svg, coa) {
- const clone = svg.cloneNode(true); // clone svg
- const d = clone.getElementsByTagName("defs")[0];
-
- d.insertAdjacentHTML("beforeend", document.getElementById(coa.shield).outerHTML); // copy shield to defs
- svg.querySelectorAll("[fill^=url]").forEach(el => {
- const id = el.getAttribute("fill").match(/\#([^)]+)\)/)[1];
- d.insertAdjacentHTML("beforeend", document.getElementById(id).outerHTML);
- });
-
- const serialized = (new XMLSerializer()).serializeToString(clone);
+ function getURL(svg, coa, size) {
+ const serialized = getSVG(svg, coa, size);
const blob = new Blob([serialized], { type: 'image/svg+xml;charset=utf-8' });
const url = window.URL.createObjectURL(blob);
return url;
}
+ function getSVG(svg, coa, size) {
+ const clone = svg.cloneNode(true); // clone svg
+ const d = clone.getElementsByTagName("defs")[0];
+
+ clone.removeAttribute("id");
+ clone.setAttribute("width", size);
+ clone.setAttribute("height", size);
+
+ d.insertAdjacentHTML("beforeend", document.getElementById(coa.shield).outerHTML); // copy shield to defs
+ clone.querySelectorAll("[fill^=url]").forEach(el => {
+ const id = el.getAttribute("fill").match(/\#([^)]+)\)/)[1];
+ d.insertAdjacentHTML("beforeend", document.getElementById(id).outerHTML);
+ });
+
+ return (new XMLSerializer()).serializeToString(clone);
+ }
+
+ function downloadGallery() {
+ const name = getFileName("Emblems Gallery");
+ const validStates = states.filter(s => s.i && !s.removed && s.coa);
+ const validProvinces = provinces.filter(p => p.i && !p.removed && p.coa);
+ const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa);
+ triggerCOALoad(validStates, validProvinces, validBurgs);
+ const timeout = (validStates.length + validProvinces.length + validBurgs.length) * 8;
+ tip("Preparing to download...", true, "warn", timeout);
+ d3.timeout(runDownload, timeout);
+
+ function runDownload() {
+ const stateSection = `
States
` + validStates.map(state => {
+ const el = document.getElementById("stateCOA"+state.i);
+ const svg = getSVG(el, state.coa, 200);
+ return `
${state.fullName}${svg}`;
+ }).join("") + `
`;
+
+ const provinceSections = validStates.map(state => {
+ const figures = validProvinces.filter(p => p.state === state.i).map(province => {
+ const el = document.getElementById("provinceCOA"+province.i);
+ const svg = getSVG(el, province.coa, 200);
+ return `
${province.fullName}${svg}`;
+ }).join("");
+ return `
${state.fullName} provinces
${figures}`;
+ }).join("");
+
+ const burgSections = validStates.map(state => {
+ const stateBurgs = validBurgs.filter(b => b.state === state.i);
+ let stateBurgSections = validProvinces.filter(p => p.state === state.i).map(province => {
+ const provinceBurgs = stateBurgs.filter(b => cells.province[b.cell] === province.i);
+ if (!provinceBurgs.length) return "";
+ const provinceBurgFigures = provinceBurgs.map(burg => {
+ const el = document.getElementById("burgCOA"+burg.i);
+ const svg = getSVG(el, burg.coa, 200);
+ return `
${burg.name}${svg}`;
+ }).join("");
+ return `
${province.fullName} burgs
${provinceBurgFigures}`;
+ }).join("");
+
+ const stateBurgOutOfProvinces = stateBurgs.filter(b => !cells.province[b.cell]);
+ const stateBurgOutOfProvincesFigures = stateBurgOutOfProvinces.map(burg => {
+ const el = document.getElementById("burgCOA"+burg.i);
+ const svg = getSVG(el, burg.coa, 200);
+ return `
${burg.name}${svg}`;
+ }).join("");
+ if (stateBurgOutOfProvincesFigures) stateBurgSections += `
${state.fullName} burgs under direct control
${stateBurgOutOfProvincesFigures}`;
+ return stateBurgSections;
+ }).join("");
+
+ const neutralBurgs = validBurgs.filter(b => !b.state);
+ const neutralsSection = neutralBurgs.length ? "
Independent burgs
" + neutralBurgs.map(burg => {
+ const el = document.getElementById("burgCOA"+burg.i);
+ const svg = getSVG(el, burg.coa, 200);
+ return `${burg.name}${svg}`;
+ }).join("") + "" : "";
+
+ const FMG = `
Azgaar's Fantasy Map Generator`;
+ const license = `
the license`;
+ const html = `
${mapName.value} Emblems Gallery
+
+
+
+
${mapName.value} Emblems Gallery
+ ${stateSection}
+ ${provinceSections}
+ ${burgSections}
+ ${neutralsSection}
+
Generated by ${FMG}. The tool is free, but images may be copyrighted, see ${license}
+ `;
+ downloadFile(html, name + ".html", "text/plain");
+ }
+ }
+
+ function triggerCOALoad(states, provinces, burgs) {
+ states.forEach(state => COArenderer.trigger("stateCOA"+state.i, state.coa));
+ provinces.forEach(province => COArenderer.trigger("provinceCOA"+province.i, province.coa));
+ burgs.forEach(burg => COArenderer.trigger("burgCOA"+burg.i, burg.coa));
+ }
+
function dragEmblem() {
const tr = parseTransform(this.getAttribute("transform"));
const x = +tr[0] - d3.event.x, y = +tr[1] - d3.event.y;
-
+
d3.event.on("drag", function() {
const transform = `translate(${(x + d3.event.x)},${(y + d3.event.y)})`;
this.setAttribute("transform", transform);
diff --git a/modules/ui/general.js b/modules/ui/general.js
index 16f5d221..99a7d3fc 100644
--- a/modules/ui/general.js
+++ b/modules/ui/general.js
@@ -330,7 +330,7 @@ document.querySelectorAll("[data-locked]").forEach(function(e) {
event.stopPropagation();
});
- e.addEventListener("click", function(event) {
+ e.addEventListener("click", function() {
const id = (this.id).slice(5);
if (this.className === "icon-lock") unlock(id);
else lock(id);
diff --git a/modules/ui/options.js b/modules/ui/options.js
index ed20fdf8..63a7f75f 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -87,8 +87,8 @@ function showSupporters() {
Jeppe Skov Jensen,María Martín López,Martin Seeger,Annie Rishor,Aram Sabatés,MadNomadMedia,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,
Thirty-OneR ,ThatGuyGW ,Dee Chiu,MontyBoosh ,Achillain ,Jaden ,SashaTK,Steve Johnson,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,Thirty-OneR,
ThatGuyGW,Dee Chiu,MontyBoosh,Achillain,Jaden,SashaTK,Steve Johnson,Pierrick Bertrand,Jared Kennedy,Dylan Devenny,Kyle Robertson,Andrew Rostaing,Daniel Gill,
- Char, Jack, Barna Csíkos, Ian Rousseau, Nicholas Grabstas, Tom Van Orden jr, Bryan Brake, Akylos, Riley Seaman,
- MaxOliver, Evan-DiLeo, Alex Debus, Joshua Vaught, Kyle S, Eric Moore, Dean Dunakin, Uniquenameosaurus, WarWizardGames`;
+ Char,Jack,Barna Csíkos,Ian Rousseau,Nicholas Grabstas,Tom Van Orden jr,Bryan Brake,Akylos,Riley Seaman,MaxOliver,Evan-DiLeo,Alex Debus,Joshua Vaught,
+ Kyle S,Eric Moore,Dean Dunakin,Uniquenameosaurus,WarWizardGames,Chance Mena,Jan Ka`;
const array = supporters.replace(/(?:\r\n|\r|\n)/g, "").split(",").map(v => capitalize(v.trim())).sort();
alertMessage.innerHTML = "
" + array.map(n => `- ${n}
`).join("") + "
";