Files
InfantrySkillCalculator/templates/modals/full_calc.html
MaxJa4 7338d34765
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 49s
Bugfixes and tooltips for full-calc ui.
2024-01-25 15:36:19 +01:00

345 lines
16 KiB
HTML

{{ define "full_calc" }}
<div class="modal modal-lg fade" data-bs-backdrop="static" data-bs-keyboard="false" id="fullCalcModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-3 text-secondary-emphasis fw-bold" id="fullCalcModalLabel">Infantryskill-Ergebnis</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body px-0">
<div class="row px-2">
<div class="col-6 border-end border-secondary px-1 pb-1" id="home-clan-results">
<h3 class="pb-3 text-center text-primary border-bottom border-primary-subtle fw-bold" id="home-clan-name">a</h3>
<!-- Dynamically added -->
</div>
<div class="col-6 px-1 mb-1" id="opponent-clan-results">
<h3 class="pb-3 text-center text-danger border-bottom border-danger-subtle fw-bold" id="opp-clan-name">b</h3>
<!-- Dynamically added -->
</div>
</div>
<div class="text-light-emphasis">
<hr class="mt-0">
</div>
<div class="row justify-content-center">
<div class="col-3 text-center fs-5 me-4 ps-0">
<img src="../static/icons/average.svg" alt="Avg" class="img-fluid me-1 align-top" style="height: 1.8rem; filter: invert(0.85);" data-bs-action="tooltip" data-bs-title="Durchschnitt Heim-Team" />
<span id="home-avg-score" class="text-warning">
<i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i>
</span>
</div>
<div class="col-2 text-center fs-5">
<i class="bi bi-plus-slash-minus me-2" data-bs-action="tooltip" data-bs-title="Differenz der Durchschnitte" ></i>
<span id="diff-score" class="text-warning">
<i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i>
</span>
</div>
<div class="col-3 text-center fs-5 ms-4 pe-0">
<img src="../static/icons/average.svg" alt="Avg" class="img-fluid me-1 align-top" style="height: 1.8rem; filter: invert(0.85);" data-bs-action="tooltip" data-bs-title="Durchschnitt Gegner-Team" />
<span id="opp-avg-score" class="text-warning">
<i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i>
</span>
</div>
</div>
</div>
<div class="modal-footer justify-content-between">
<div class="text-center text-secondary ms-3" id="fullCalcInfo" hidden></div>
<div class="progress w-100" style="height: 30px;" id="fullCalcProgressbar" role="progressbar">
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 0">Lade...</div>
</div>
<button type="submit" name="submit" class="btn btn-lg btn-outline-primary" onclick="downloadScreenshot()" data-bs-action="tooltip" data-bs-title="Als Bild herunterladen" hidden>
<i class="bi bi-download"></i>
</button>
</div>
</div>
</div>
</div>
<ul class="list-group list-group-horizontal fs-5 px-2 visually-hidden" id="result-item">
<li class="list-group-item border-0 rounded-0 border-bottom border-secondary-subtle w-75 text-light">
<i class="bi bi-question-square text-secondary me-2"></i>
<span id="username"></span>
</li>
<li class="list-group-item border-0 rounded-0 border-bottom border-secondary-subtle text-end w-25 ps-1 pe-3 text-warning">
<i class="bi bi-trophy text-warning me-2" hidden></i>
<span id="score"></span>
</li>
</ul>
<script lang="javascript">
function getSelectedPlayers(listId) {
const list = document.getElementById(listId);
const selectedPlayerNodes = list.querySelectorAll('input[type="checkbox"]:checked');
const selectedPlayers = [];
selectedPlayerNodes.forEach(p => {
p = p.parentElement.parentElement.parentElement.querySelector('span');
selectedPlayers.push([p.parentElement.getAttribute("data-id"), p.innerText]);
});
return selectedPlayers;
}
function getAndInsertScore(playerID, resultList) {
return fetch('/score/' + playerID)
.then(response => response.text())
.then(text => {
const score = resultList.querySelector('ul[data-id="' + playerID + '"] #score');
score.innerHTML = text;
if (!isNaN(parseFloat(text)))
score.previousElementSibling.hidden = false;
else
initTooltips(score);
return text;
});
}
function downloadScreenshot() {
const submitBtn = document.querySelector('#fullCalcModal button[name="submit"]');
submitBtn.hidden = true;
html2canvas(document.querySelector('#fullCalcModal .modal-content'), {
backgroundColor: null,
scale: 1
}).then(canvas => {
let link = document.createElement('a');
let date = new Date().toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }).replace(', ', '_').replace(':', '-');
link.download = 'InfantrySkill_' + date + '.png';
link.href = canvas.toDataURL();
link.click();
submitBtn.hidden = false;
});
}
document.addEventListener('DOMContentLoaded', function() {
const fullCalcModal = document.getElementById('fullCalcModal');
const downloadBtn = fullCalcModal.querySelector('button[name="submit"]');
const progressbar = document.getElementById('fullCalcProgressbar');
const resultItem = document.getElementById('result-item');
const homeClanResults = document.getElementById('home-clan-results');
const oppClanResults = document.getElementById('opponent-clan-results');
const homeClanList = document.getElementById('home-clan');
const oppClanList = document.getElementById('opponent-clan');
const spinnerBorder = '<i class="spinner-border spinner-border-sm text-warning align-baseline me-2" style="margin-left: 0.91rem;" role="status"></i>';
const spinnerGrow = '<i class="spinner-grow spinner-grow-sm text-secondary align-baseline mx-2" role="status"></i>';
const dash = '<i class="bi bi-dash mx-2"></i>';
const homeAvgScore = document.getElementById('home-avg-score');
const diffScore = document.getElementById('diff-score');
const oppAvgScore = document.getElementById('opp-avg-score');
const fullCalcInfo = document.getElementById('fullCalcInfo');
let calcMutex = false;
let sortMutex = false;
function addResultItem(resultList, playerID, playerName) {
let item = resultList.appendChild(resultItem.cloneNode(true));
item.querySelector('#username').innerText = playerName;
item.querySelector('#score').innerHTML = spinnerBorder;
item.setAttribute('data-id', playerID);
item.classList.remove('visually-hidden');
}
function calcStatistics() {
if (calcMutex) {
setTimeout(calcStatistics, 100);
return;
}
calcMutex = true;
let homeScores = [];
let oppScores = [];
homeClanResults.querySelectorAll('ul').forEach(e => {
let score = parseFloat(e.querySelector('#score').innerText);
if (!isNaN(score)) {
homeScores.push(score);
}
});
oppClanResults.querySelectorAll('ul').forEach(e => {
let score = parseFloat(e.querySelector('#score').innerText);
if (!isNaN(score)) {
oppScores.push(score);
}
});
homeScores.sort((a, b) => a - b);
oppScores.sort((a, b) => a - b);
let homeAvg = homeScores.reduce((a, b) => a + b, 0) / homeScores.length;
let oppAvg = oppScores.reduce((a, b) => a + b, 0) / oppScores.length;
if (!isNaN(homeAvg))
homeAvgScore.innerText = homeAvg.toFixed(2);
if (!isNaN(oppAvg))
oppAvgScore.innerText = oppAvg.toFixed(2);
if (!isNaN(homeAvg) && !isNaN(oppAvg))
diffScore.innerText = (homeAvg - oppAvg).toFixed(2);
calcMutex = false;
}
function sortPlayers() {
if (sortMutex) {
setTimeout(sortPlayers, 100);
return;
}
sortMutex = true;
let homePlayers = Array.from(homeClanResults.querySelectorAll('ul'));
let oppPlayers = Array.from(oppClanResults.querySelectorAll('ul'));
homePlayers.sort((a, b) => {
let aScore = parseFloat(a.querySelector('#score').innerText);
let bScore = parseFloat(b.querySelector('#score').innerText);
if (isNaN(aScore)) {
return 1;
} else if (isNaN(bScore)) {
return -1;
} else {
return bScore - aScore;
}
});
oppPlayers.sort((a, b) => {
let aScore = parseFloat(a.querySelector('#score').innerText);
let bScore = parseFloat(b.querySelector('#score').innerText);
if (isNaN(aScore)) {
return 1;
} else if (isNaN(bScore)) {
return -1;
} else {
return bScore - aScore;
}
});
homePlayers.forEach(p => homeClanResults.appendChild(p));
oppPlayers.forEach(p => oppClanResults.appendChild(p));
sortMutex = false;
}
function setSquadNumbers() {
let homePlayers = Array.from(homeClanResults.querySelectorAll('ul'));
let oppPlayers = Array.from(oppClanResults.querySelectorAll('ul'));
let playerCounter = 0;
function setSquadNumber(player, squadNum) {
player.querySelector('#username').previousElementSibling.classList.replace('bi-question-square', 'bi-' + squadNum + '-square');
}
homePlayers.forEach((p, _) => {
if (p.querySelector('#score').innerText === '' && p.querySelector('#score').innerHTML !== spinnerBorder) {
setSquadNumber(p, 'x');
} else {
let squadNum = Math.floor(playerCounter / 4) + 1;
setSquadNumber(p, squadNum);
playerCounter++;
}
});
playerCounter = 0;
oppPlayers.forEach((p, _) => {
if (p.querySelector('#score').innerText === '' && p.querySelector('#score').innerHTML !== spinnerBorder) {
setSquadNumber(p, 'x');
} else {
let squadNum = Math.floor(playerCounter / 4) + 1;
setSquadNumber(p, squadNum);
playerCounter++;
}
});
}
function cleanupStatistic(statisticField) {
if (statisticField.innerHTML.trim() === spinnerGrow)
statisticField.innerHTML = dash;
}
function updateScoreInClanList(clanListId, playerID, score) {
let clanPlayers = document.getElementById(clanListId).querySelectorAll('div[data-id]');
clanPlayers.forEach(p => {
if (p.getAttribute('data-id') === playerID) {
p.querySelector('#quickScore').innerHTML = score;
}
});
}
function getClanName(clanList) {
let clanName = clanList.options[clanList.selectedIndex].innerText;
return clanList.selectedIndex <= 0 ? dash : clanName.replace(/\[.*]/, '').trim();
}
function updateProgress(progress) {
progressbar.firstElementChild.style.width = progress + '%';
}
if (fullCalcModal) {
fullCalcModal.addEventListener('show.bs.modal', _ => {
let homePlayers = getSelectedPlayers('home-player-list');
let oppPlayers = getSelectedPlayers('opponent-player-list');
let promises = [];
let doneCounter = 0;
homeClanResults.querySelector('#home-clan-name').innerHTML = getClanName(homeClanList);
oppClanResults.querySelector('#opp-clan-name').innerHTML = getClanName(oppClanList);
homePlayers.forEach(p => {
addResultItem(homeClanResults, p[0], p[1]);
promises.push(
getAndInsertScore(p[0], homeClanResults)
.then(score => {
calcStatistics();
sortPlayers();
updateScoreInClanList('home-player-list', p[0], score);
updateProgress(100 * (++doneCounter / (homePlayers.length + oppPlayers.length)));
})
);
});
oppPlayers.forEach(p => {
addResultItem(oppClanResults, p[0], p[1]);
promises.push(
getAndInsertScore(p[0], oppClanResults)
.then(score => {
calcStatistics();
sortPlayers();
updateScoreInClanList('opponent-player-list', p[0], score);
updateProgress(100 * (++doneCounter / (homePlayers.length + oppPlayers.length)));
})
);
});
Promise.all(promises).then(_ => {
let liItems = homeClanResults.querySelectorAll('li');
if (liItems.length > 1) {
liItems[liItems.length - 1].classList.remove('border-bottom');
liItems[liItems.length - 2].classList.remove('border-bottom');
}
liItems = oppClanResults.querySelectorAll('li');
if (liItems.length > 1) {
liItems[liItems.length - 1].classList.remove('border-bottom');
liItems[liItems.length - 2].classList.remove('border-bottom');
}
setSquadNumbers();
cleanupStatistic(homeAvgScore);
cleanupStatistic(diffScore);
cleanupStatistic(oppAvgScore);
progressbar.hidden = true;
downloadBtn.hidden = false;
fullCalcInfo.hidden = false;
fullCalcInfo.innerHTML = 'Erstellt von <b>{{ .Username }}</b> am ' +
new Date().toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short' }) + ' Uhr';
});
});
fullCalcModal.addEventListener('hidden.bs.modal', _ => {
homeClanResults.querySelectorAll('ul').forEach(e => e.remove());
oppClanResults.querySelectorAll('ul').forEach(e => e.remove());
homeAvgScore.innerHTML = spinnerGrow;
diffScore.innerHTML = spinnerGrow;
oppAvgScore.innerHTML = spinnerGrow;
downloadBtn.hidden = true;
fullCalcInfo.hidden = true;
progressbar.hidden = false;
progressbar.firstElementChild.style.width = '0';
});
}
});
</script>
{{ end }}