From 378ed71702fc1eb40fc7cfe1965b54a71c9db64e Mon Sep 17 00:00:00 2001 From: Azgaar Date: Thu, 4 Feb 2021 02:17:00 +0300 Subject: [PATCH] v1.5.09 - speak functionality --- icons.css | 13 +----- index.css | 15 +++++-- index.html | 72 ++++++++++++++++++++++++---------- modules/coa-generator.js | 6 +-- modules/coa-renderer.js | 2 +- modules/ui/burg-editor.js | 40 +++++++++++++------ modules/ui/general.js | 16 ++++++++ modules/ui/namesbase-editor.js | 1 + modules/ui/notes-editor.js | 3 +- modules/ui/options.js | 26 ++++++++++++ modules/ui/provinces-editor.js | 2 +- modules/ui/states-editor.js | 2 +- 12 files changed, 143 insertions(+), 55 deletions(-) diff --git a/icons.css b/icons.css index eab23067..008faebc 100644 --- a/icons.css +++ b/icons.css @@ -252,17 +252,8 @@ .icon-if:before {font-style: italic; font-weight: bold;content:'if';} .icon-coa:before {content:'\f3ed'; font-size: .9em; color: #999;} /* '' */ .icon-half:before {font-weight: bold;content:'½';} -.icon-curve:before {content: 'C';} -.icon-area:before {content: 'O';} -.icon-curve:before, -.icon-area:before { - font-size: 1.5em; - padding: 0; - writing-mode: tb-rl; - margin-left: 1px; - width: .6em; - font-family: monospace; -} +.icon-voice:before {content:'🔊';} + .icon-die:before {content:'🎲';} .icon-button-die:before {content:'🎲'; padding-right: .4em;} .icon-button-power:before {content:'💪'; padding-right: .6em;} diff --git a/index.css b/index.css index a7e09155..386e2ba5 100644 --- a/index.css +++ b/index.css @@ -76,10 +76,14 @@ input, button, select, a, textarea { outline: none; } -button, select, a, .pointer { +button, select, a { cursor: pointer; } +.pointer { + cursor: pointer !important; +} + #prec text { font-size: 32px; stroke: none; @@ -1464,9 +1468,7 @@ div.states > .coaIcon > use { } .burgFeature { - font-size: 1.2em; - padding: 1px 2px; - color: #555; + padding: 1px; cursor: pointer; } @@ -2141,6 +2143,11 @@ svg.button { border: dashed 1px #5d4651; } +.speaker { + font-size: .9em; + cursor: pointer; +} + #prompt { position: absolute; left: 50%; diff --git a/index.html b/index.html index 03b5c1a7..31d5b62f 100644 --- a/index.html +++ b/index.html @@ -1863,6 +1863,17 @@ + + + Speaker voice + + + + + 🔊 + + + Zoom extent @@ -2163,6 +2174,7 @@ @@ -2186,6 +2198,7 @@ @@ -3285,9 +3313,11 @@ Object name: + 🔊
+ diff --git a/modules/coa-generator.js b/modules/coa-generator.js index 2cf3de72..d8bab364 100644 --- a/modules/coa-generator.js +++ b/modules/coa-generator.js @@ -361,9 +361,9 @@ coa.ordinaries ? coa.ordinaries.push(canton) : coa.ordinaries = [canton]; - console.log(encodeURI(`https://azgaar.github.io/Armoria/?coa=${JSON.stringify(coa)}`)); - console.log(encodeURI(`https://azgaar.github.io/Armoria/?coa=${JSON.stringify(parent)}`)); - console.log("-------"); + // console.log(encodeURI(`https://azgaar.github.io/Armoria/?coa=${JSON.stringify(coa)}`)); + // console.log(encodeURI(`https://azgaar.github.io/Armoria/?coa=${JSON.stringify(parent)}`)); + // console.log("-------"); } function selectCharge(set) { diff --git a/modules/coa-renderer.js b/modules/coa-renderer.js index 8c212658..1dd3cb81 100644 --- a/modules/coa-renderer.js +++ b/modules/coa-renderer.js @@ -879,7 +879,7 @@ // async render coa if it does not exist const trigger = function(id, coa) { if (!coa) { - console.warn(id, "emblem is undefined"); + console.warn(`Emblem ${id} is undefined`); return; } if (!document.getElementById(id)) draw(id, coa); diff --git a/modules/ui/burg-editor.js b/modules/ui/burg-editor.js index 08c283d8..38c2d886 100644 --- a/modules/ui/burg-editor.js +++ b/modules/ui/burg-editor.js @@ -31,8 +31,9 @@ function editBurg(id) { document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup); document.getElementById("burgName").addEventListener("input", changeName); - document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture); document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom); + document.getElementById("burgCulture").addEventListener("input", changeCulture); + document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture); document.getElementById("burgPopulation").addEventListener("change", changePopulation); burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature)); @@ -43,7 +44,7 @@ function editBurg(id) { document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle); document.getElementById("burgSeeInMFCG").addEventListener("click", openInMFCG); - document.getElementById("burgOpenCOA").addEventListener("click", editCOA); + document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit); document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg); document.getElementById("burglLegend").addEventListener("click", editBurgLegend); document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg); @@ -55,6 +56,12 @@ function editBurg(id) { document.getElementById("burgPopulation").value = rn(b.population * populationRate.value * urbanization.value); document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none"; + // update list and select culture + const cultureSelect = document.getElementById("burgCulture"); + cultureSelect.options.length = 0; + const cultures = pack.cultures.filter(c => !c.removed); + cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture))); + // toggle features if (b.capital) document.getElementById("burgCapital").classList.remove("inactive"); else document.getElementById("burgCapital").classList.add("inactive"); @@ -79,6 +86,11 @@ function editBurg(id) { burgLabels.selectAll("g").each(function() { select.options.add(new Option(this.id, this.id, false, this.id === group)); }); + + // set emlem image + const coaID = "burgCOA"+id; + COArenderer.trigger(coaID, b.coa); + document.getElementById("burgEmblem").setAttribute("href", "#" + coaID); } function dragBurgLabel() { @@ -220,6 +232,17 @@ function editBurg(id) { elSelected.text(burgName.value); } + function generateNameRandom() { + const base = rand(nameBases.length-1); + burgName.value = Names.getBase(base); + changeName(); + } + + function changeCulture() { + const id = +elSelected.attr("data-id"); + pack.burgs[id].culture = +this.value; + } + function generateNameCulture() { const id = +elSelected.attr("data-id"); const culture = pack.burgs[id].culture; @@ -227,12 +250,6 @@ function editBurg(id) { changeName(); } - function generateNameRandom() { - const base = rand(nameBases.length-1); - burgName.value = Names.getBase(base); - changeName(); - } - function changePopulation() { const id = +elSelected.attr("data-id"); pack.burgs[id].population = rn(burgPopulation.value / populationRate.value / urbanization.value, 4); @@ -326,10 +343,9 @@ function editBurg(id) { } } - function editCOA() { - const id = elSelected.attr("data-id"), burg = pack.burgs[id]; - const coa = COA.toString(burg.coa); - openURL("http://azgaar.github.io/Armoria/?coa=" + coa); + function openEmblemEdit() { + const id = +elSelected.attr("data-id"), burg = pack.burgs[id]; + editEmblem("burg", "burgCOA"+id, burg); } function toggleRelocateBurg() { diff --git a/modules/ui/general.js b/modules/ui/general.js index 99a7d3fc..03a1cbfb 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -367,6 +367,22 @@ function stored(option) { return localStorage.getItem(option); } +// assign skeaker behaviour +Array.from(document.getElementsByClassName("speaker")).forEach(el => { + const input = el.previousElementSibling; + el.addEventListener("click", () => speak(input.value)); +}); + +function speak(text) { + const speaker = new SpeechSynthesisUtterance(text); + const voices = speechSynthesis.getVoices(); + if (voices.length) { + const voiceId = +document.getElementById("speakerVoice").value; + speaker.voice = voices[voiceId]; + } + speechSynthesis.speak(speaker); +} + // apply drop-down menu option. If the value is not in options, add it function applyOption(select, id, name = id) { const custom = !Array.from(select.options).some(o => o.value == id); diff --git a/modules/ui/namesbase-editor.js b/modules/ui/namesbase-editor.js index baf0acb5..03149a13 100644 --- a/modules/ui/namesbase-editor.js +++ b/modules/ui/namesbase-editor.js @@ -22,6 +22,7 @@ function editNamesbase() { document.getElementById("namesbaseDownload").addEventListener("click", namesbaseDownload); document.getElementById("namesbaseUpload").addEventListener("click", () => namesbaseToLoad.click()); document.getElementById("namesbaseToLoad").addEventListener("change", function() {uploadFile(this, namesbaseUpload)}); + document.getElementById("namesbaseSpeak").addEventListener("click", () => speak(namesbaseExamples.textContent)); createBasesList(); updateInputs(); diff --git a/modules/ui/notes-editor.js b/modules/ui/notes-editor.js index c273c61e..caa4a59f 100644 --- a/modules/ui/notes-editor.js +++ b/modules/ui/notes-editor.js @@ -38,7 +38,7 @@ function editNotes(id, name) { // open a dialog $("#notesEditor").dialog({ - title: "Notes Editor", minWidth: "40em", + title: "Notes Editor", minWidth: "40em", width: "50vw", position: {my: "center", at: "center", of: "svg"}, close: () => notesText.innerHTML = "" }); @@ -50,6 +50,7 @@ function editNotes(id, name) { document.getElementById("notesSelect").addEventListener("change", changeObject); document.getElementById("notesName").addEventListener("input", changeName); document.getElementById("notesPin").addEventListener("click", () => options.pinNotes = !options.pinNotes); + document.getElementById("notesSpeak").addEventListener("click", () => speak(editor.content.innerHTML)); document.getElementById("notesFocus").addEventListener("click", validateHighlightElement); document.getElementById("notesDownload").addEventListener("click", downloadLegends); document.getElementById("notesUpload").addEventListener("click", () => legendsToLoad.click()); diff --git a/modules/ui/options.js b/modules/ui/options.js index 63a7f75f..b6e6a139 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -140,6 +140,7 @@ optionsContent.addEventListener("click", function(event) { else if (id === "optionsEraRegenerate") regenerateEra(); else if (id === "zoomExtentDefault") restoreDefaultZoomExtent(); else if (id === "translateExtent") toggleTranslateExtent(event.target); + else if (id === "speakerTest") testSpeaker(); }); function mapSizeInputChange() { @@ -198,6 +199,30 @@ function toggleTranslateExtent(el) { else zoom.translateExtent([[0, 0], [graphWidth, graphHeight]]); } +// add voice options +const voiceInterval = setInterval(function() { + const voices = speechSynthesis.getVoices(); + if (voices.length) clearInterval(voiceInterval); else return; + + const select = document.getElementById("speakerVoice"); + voices.forEach((voice, i) => { + select.options.add(new Option(voice.name, i, false)); + }); + if (stored("speakerVoice")) select.value = localStorage.getItem("speakerVoice"); // se voice to store + else select.value = voices.findIndex(voice => voice.lang === "en-US"); // or to first found English-US +}, 1000); + +function testSpeaker() { + const text = `${mapName.value}, ${options.year} ${options.era}`; + const speaker = new SpeechSynthesisUtterance(text); + const voices = speechSynthesis.getVoices(); + if (voices.length) { + const voiceId = +document.getElementById("speakerVoice").value; + speaker.voice = voices[voiceId]; + } + speechSynthesis.speak(speaker); +} + function generateMapWithSeed() { if (optionsSeed.value == seed) { tip("The current map already has this seed", false, "error"); @@ -331,6 +356,7 @@ function applyStoredOptions() { for (let i=0; i < localStorage.length; i++) { const stored = localStorage.key(i), value = localStorage.getItem(stored); + if (stored === "speakerVoice") continue; const input = document.getElementById(stored+"Input") || document.getElementById(stored); const output = document.getElementById(stored+"Output"); if (input) input.value = value; diff --git a/modules/ui/provinces-editor.js b/modules/ui/provinces-editor.js index 44a16e9c..54893fa2 100644 --- a/modules/ui/provinces-editor.js +++ b/modules/ui/provinces-editor.js @@ -383,7 +383,7 @@ function editProvinces() { document.getElementById("provinceNameEditorFull").value = p.fullName; $("#provinceNameEditor").dialog({ - resizable: false, title: "Change province name", width: "22em", buttons: { + resizable: false, title: "Change province name", buttons: { Apply: function() {applyNameChange(p); $(this).dialog("close");}, Cancel: function() {$(this).dialog("close");} }, position: {my: "center", at: "center", of: "svg"} diff --git a/modules/ui/states-editor.js b/modules/ui/states-editor.js index 99a0c02a..7e87b7cc 100644 --- a/modules/ui/states-editor.js +++ b/modules/ui/states-editor.js @@ -232,7 +232,7 @@ function editStates() { document.getElementById("stateNameEditorFull").value = s.fullName || ""; $("#stateNameEditor").dialog({ - resizable: false, title: "Change state name", width: "22em", buttons: { + resizable: false, title: "Change state name", buttons: { Apply: function() {applyNameChange(s); $(this).dialog("close");}, Cancel: function() {$(this).dialog("close");} }, position: {my: "center", at: "center", of: "svg"}