'use strict'; function editEmblem(type, id, el) { if (customization) return; if (!id && d3.event) defineEmblemData(d3.event); emblems.selectAll('use').call(d3.drag().on('drag', dragEmblem)).classed('draggable', true); const emblemStates = document.getElementById('emblemStates'); const emblemProvinces = document.getElementById('emblemProvinces'); const emblemBurgs = document.getElementById('emblemBurgs'); const emblemShapeSelector = document.getElementById('emblemShapeSelector'); updateElementSelectors(type, id, el); $('#emblemEditor').dialog({ title: 'Edit Emblem', resizable: true, width: '18.2em', height: 'auto', position: {my: 'left top', at: 'left+10 top+10', of: 'svg', collision: 'fit'}, close: closeEmblemEditor }); // add listeners,then remove on closure emblemStates.oninput = selectState; emblemProvinces.oninput = selectProvince; emblemBurgs.oninput = selectBurg; emblemShapeSelector.oninput = changeShape; document.getElementById('emblemSizeSlider').oninput = changeSize; document.getElementById('emblemSizeNumber').oninput = changeSize; document.getElementById('emblemsRegenerate').onclick = regenerate; document.getElementById('emblemsArmoria').onclick = openInArmoria; document.getElementById('emblemsUpload').onclick = toggleUpload; document.getElementById('emblemsUploadImage').onclick = () => imageToLoad.click(); document.getElementById('emblemsUploadSVG').onclick = () => svgToLoad.click(); document.getElementById('imageToLoad').onchange = () => uploadImage('image'); document.getElementById('svgToLoad').onchange = () => uploadImage('svg'); 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) { const parent = e.target.parentNode; const [g, t] = parent.id === 'burgEmblems' ? [pack.burgs, 'burg'] : parent.id === 'provinceEmblems' ? [pack.provinces, 'province'] : [pack.states, 'state']; const i = +e.target.dataset.i; type = t; id = type + 'COA' + i; el = g[i]; } function updateElementSelectors(type, id, el) { let state = 0, province = 0, burg = 0; // set active type emblemStates.parentElement.className = type === 'state' ? 'active' : ''; emblemProvinces.parentElement.className = type === 'province' ? 'active' : ''; emblemBurgs.parentElement.className = type === 'burg' ? 'active' : ''; // define selected values if (type === 'state') state = el.i; else if (type === 'province') { province = el.i; state = pack.states[el.state].i; } else { burg = el.i; province = pack.cells.province[el.cell] ? pack.provinces[pack.cells.province[el.cell]].i : 0; state = el.state; } const validBurgs = pack.burgs.filter((burg) => burg.i && !burg.removed && burg.coa); // update option list and select actual values emblemStates.options.length = 0; const neutralBurgs = validBurgs.filter((burg) => !burg.state); if (neutralBurgs.length) emblemStates.options.add(new Option(pack.states[0].name, 0, false, !state)); const stateList = pack.states.filter((state) => state.i && !state.removed); stateList.forEach((s) => emblemStates.options.add(new Option(s.name, s.i, false, s.i === state))); emblemProvinces.options.length = 0; emblemProvinces.options.add(new Option('', 0, false, !province)); const provinceList = pack.provinces.filter((province) => !province.removed && province.state === state); provinceList.forEach((p) => emblemProvinces.options.add(new Option(p.name, p.i, false, p.i === province))); emblemBurgs.options.length = 0; emblemBurgs.options.add(new Option('', 0, false, !burg)); const burgList = validBurgs.filter((burg) => (province ? pack.cells.province[burg.cell] === province : burg.state === state)); burgList.forEach((b) => emblemBurgs.options.add(new Option(b.capital ? '👑 ' + b.name : b.name, b.i, false, b.i === burg))); emblemBurgs.options[0].disabled = true; COArenderer.trigger(id, el.coa); updateEmblemData(type, id, el); } function updateEmblemData(type, id, el) { if (!el.coa) return; document.getElementById('emblemImage').setAttribute('href', '#' + id); let name = el.fullName || el.name; if (type === 'burg') name = 'Burg of ' + name; document.getElementById('emblemArmiger').innerText = name; if (el.coa === 'custom') emblemShapeSelector.disabled = true; else { emblemShapeSelector.disabled = false; emblemShapeSelector.value = el.coa.shield; } const size = el.coaSize || 1; document.getElementById('emblemSizeSlider').value = size; document.getElementById('emblemSizeNumber').value = size; } function selectState() { const state = +this.value; if (state) { type = 'state'; el = pack.states[state]; id = 'stateCOA' + state; } else { // select neutral burg if state is changed to Neutrals const neutralBurgs = pack.burgs.filter((burg) => burg.i && !burg.removed && !burg.state); if (!neutralBurgs.length) return; type = 'burg'; el = neutralBurgs[0]; id = 'burgCOA' + neutralBurgs[0].i; } updateElementSelectors(type, id, el); } function selectProvince() { const province = +this.value; if (province) { type = 'province'; el = pack.provinces[province]; id = 'provinceCOA' + province; } else { // select state if province is changed to null value const state = +emblemStates.value; type = 'state'; el = pack.states[state]; id = 'stateCOA' + state; } updateElementSelectors(type, id, el); } function selectBurg() { const burg = +this.value; type = 'burg'; el = pack.burgs[burg]; id = 'burgCOA' + burg; updateElementSelectors(type, id, el); } function changeShape() { el.coa.shield = this.value; const coaEl = document.getElementById(id); if (coaEl) coaEl.remove(); COArenderer.trigger(id, el.coa); } function showArea() { highlightEmblemElement(type, el); } function changeSize() { const size = (el.coaSize = +this.value); document.getElementById('emblemSizeSlider').value = size; document.getElementById('emblemSizeNumber').value = size; const g = emblems.select('#' + type + 'Emblems'); g.select("[data-i='" + el.i + "']").remove(); if (!size) return; // re-append use element const categotySize = +g.attr('font-size'); const shift = (categotySize * size) / 2; const x = el.x || el.pole[0]; const y = el.y || el.pole[1]; g.append('use') .attr('data-i', el.i) .attr('x', rn(x - shift), 2) .attr('y', rn(y - shift), 2) .attr('width', size + 'em') .attr('height', size + 'em') .attr('href', '#' + id); } function regenerate() { let parent = null; if (type === 'province') parent = pack.states[el.state]; else if (type === 'burg') { const province = pack.cells.province[el.cell]; parent = province ? pack.provinces[province] : pack.states[el.state]; } const shield = el.coa.shield || COA.getShield(el.culture || parent?.culture || 0, el.state); el.coa = COA.generate(parent ? parent.coa : null, 0.3, 0.1, null); el.coa.shield = shield; emblemShapeSelector.disabled = false; emblemShapeSelector.value = el.coa.shield; const coaEl = document.getElementById(id); if (coaEl) coaEl.remove(); COArenderer.trigger(id, el.coa); } function openInArmoria() { const coa = el.coa && el.coa !== 'custom' ? el.coa : {t1: 'sable'}; const json = JSON.stringify(coa).replaceAll('#', '%23'); const url = `https://azgaar.github.io/Armoria/?coa=${json}&from=FMG`; openURL(url); } function toggleUpload() { document.getElementById('emblemDownloadControl').classList.add('hidden'); const buttons = document.getElementById('emblemUploadControl'); buttons.classList.toggle('hidden'); } function uploadImage(type) { const input = type === 'image' ? document.getElementById('imageToLoad') : document.getElementById('svgToLoad'); const file = input.files[0]; input.value = ''; if (file.size > 500000) return tip(`File is too big, please optimize file size up to 500kB and re-upload. Recommended size is 200x200 px and up to 100kB`, true, 'error', 5000); const reader = new FileReader(); reader.onload = function (readerEvent) { const result = readerEvent.target.result; const defs = document.getElementById('defs-emblems'); const coa = document.getElementById(id); // old emblem if (type === 'image') { const svg = ``; defs.insertAdjacentHTML('beforeend', svg); } else { const el = document.createElement('html'); el.innerHTML = result; // remove sodipodi and inkscape attributes el.querySelectorAll('*').forEach((el) => { const attributes = el.getAttributeNames(); attributes.forEach((attr) => { if (attr.includes('inkscape') || attr.includes('sodipodi')) el.removeAttribute(attr); }); }); const svg = el.querySelector('svg'); if (!svg) return tip('The file should be prepated for load to FMG. Please use Armoria or other relevant tools', false, 'error'); const newEmblem = defs.appendChild(svg); newEmblem.id = id; newEmblem.setAttribute('width', 200); newEmblem.setAttribute('height', 200); } if (coa) coa.remove(); // remove old emblem el.coa = 'custom'; emblemShapeSelector.disabled = true; }; if (type === 'image') reader.readAsDataURL(file); else reader.readAsText(file); } function toggleDownload() { document.getElementById('emblemUploadControl').classList.add('hidden'); const buttons = document.getElementById('emblemDownloadControl'); buttons.classList.toggle('hidden'); } async function download(format) { const coa = document.getElementById(id); const size = +emblemsDownloadSize.value; const url = await getURL(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; link.click(); } function downloadRaster(format, url, link, size) { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = size; canvas.height = size; const img = new Image(); img.src = url; img.onload = function () { if (format === 'jpeg') { ctx.fillStyle = '#fff'; ctx.fillRect(0, 0, canvas.width, canvas.height); } ctx.drawImage(img, 0, 0, canvas.width, canvas.height); const dataURL = canvas.toDataURL('image/' + format, 0.92); link.href = dataURL; link.click(); window.setTimeout(() => window.URL.revokeObjectURL(dataURL), 6000); }; } async function getURL(svg, size) { const serialized = getSVG(svg, size); const blob = new Blob([serialized], {type: 'image/svg+xml;charset=utf-8'}); const url = window.URL.createObjectURL(blob); window.setTimeout(() => window.URL.revokeObjectURL(url), 6000); return url; } function getSVG(svg, size) { const clone = svg.cloneNode(true); clone.setAttribute('width', size); clone.setAttribute('height', size); return new XMLSerializer().serializeToString(clone); } async function downloadGallery() { const name = getFileName('Emblems Gallery'); const validStates = pack.states.filter((s) => s.i && !s.removed && s.coa); const validProvinces = pack.provinces.filter((p) => p.i && !p.removed && p.coa); const validBurgs = pack.burgs.filter((b) => b.i && !b.removed && b.coa); await renderAllEmblems(validStates, validProvinces, validBurgs); runDownload(); function runDownload() { const back = `Go Back`; const stateSection = `

States

` + validStates .map((state) => { const el = document.getElementById('stateCOA' + state.i); return `
${state.fullName}
${getSVG(el, 200)}
`; }) .join('') + `
`; const provinceSections = validStates .map((state) => { const stateProvinces = validProvinces.filter((p) => p.state === state.i); const figures = stateProvinces .map((province) => { const el = document.getElementById('provinceCOA' + province.i); return `
${province.fullName}
${getSVG(el, 200)}
`; }) .join(''); return stateProvinces.length ? `
${back}

${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) => pack.cells.province[b.cell] === province.i); const provinceBurgFigures = provinceBurgs .map((burg) => { const el = document.getElementById('burgCOA' + burg.i); return `
${burg.name}
${getSVG(el, 200)}
`; }) .join(''); return provinceBurgs.length ? `
${back}

${province.fullName} burgs

${provinceBurgFigures}
` : ''; }) .join(''); const stateBurgOutOfProvinces = stateBurgs.filter((b) => !pack.cells.province[b.cell]); const stateBurgOutOfProvincesFigures = stateBurgOutOfProvinces .map((burg) => { const el = document.getElementById('burgCOA' + burg.i); return `
${burg.name}
${getSVG(el, 200)}
`; }) .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); return `
${burg.name}
${getSVG(el, 200)}
`; }) .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'); } } async function renderAllEmblems(states, provinces, burgs) { tip('Preparing for download...', true, 'warn'); const statePromises = states.map((state) => COArenderer.trigger('stateCOA' + state.i, state.coa)); const provincePromises = provinces.map((province) => COArenderer.trigger('provinceCOA' + province.i, province.coa)); const burgPromises = burgs.map((burg) => COArenderer.trigger('burgCOA' + burg.i, burg.coa)); const promises = [...statePromises, ...provincePromises, ...burgPromises]; return Promise.allSettled(promises).then((res) => clearMainTip()); } 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); }); } function closeEmblemEditor() { emblems.selectAll('use').call(d3.drag().on('drag', null)).attr('class', null); } }