import i18next from 'https://cdn.jsdelivr.net/npm/i18next@25.7.4/+esm';
import i18nextHttpBackend from 'https://cdn.jsdelivr.net/npm/i18next-http-backend@3.0.2/+esm'
import i18nextBrowserLanguagedetector from 'https://cdn.jsdelivr.net/npm/i18next-browser-languagedetector@8.2.0/+esm'
let apiUrlRoot = "";
const rootDiv = document.getElementById("safca_api_data");
const cupImg = `
`
const cupImg2 = `
`
const voidFunction = () => {
}
let lastRf = 0;
let rfFonction = voidFunction;
setInterval(() => {
// rfFonction();
}, 15000);
function setSubPage(name) {
window.location.hash = name;
const location = name.split('/');
console.log(location);
switch (location[0]) {
case 'home':
homePage();
break;
case 'poule':
poulePage(location);
break;
case 'comb':
combPage(location);
break;
case 'club':
clubPage(location);
break;
case 'all':
combsPage();
break;
}
}
function homePage() {
rootDiv.innerHTML = `
${i18next.t('résultatDeLaCompétition')} :
`;
let content = document.createElement('div');
content.innerHTML = `
`
rootDiv.append(content)
document.getElementById('pouleLink').addEventListener('click', () => setSubPage('poule'));
document.getElementById('combLink').addEventListener('click', () => setSubPage('comb'));
document.getElementById('clubLink').addEventListener('click', () => setSubPage('club'));
document.getElementById('allLink').addEventListener('click', () => setSubPage('all'));
}
let loadingAnimationStep = 0;
function startLoading(root) {
const id = "loading" + Math.random().toString(36);
let element = document.createElement('h2');
element.id = id;
const anim = setInterval(() => {
let str = i18next.t('chargement');
for (let i = 0; i < loadingAnimationStep; i++) {
str += ".";
}
loadingAnimationStep = (loadingAnimationStep + 1) % 4;
element.innerText = str;
}, 500);
root.append(element)
return {interval: anim, root: root, element: element};
}
function stopLoading(loading) {
clearInterval(loading['interval']);
loading['root'].removeChild(loading['element']);
}
function scorePrint(s1) {
switch (s1) {
case -997:
return i18next.t('disc.');
case -998:
return i18next.t('abs.');
case -999:
return i18next.t('for.');
case -1000:
return "";
default:
return String(s1);
}
}
function scoreToString(score) {
return score.map(o => scorePrint(o.at(0)) + "-" + scorePrint(o.at(1))).join(" | ");
}
function dateToString(date) {
if (date === null || date === undefined)
return "";
const date_ = new Date(date);
const date_2 = new Date(date);
const current = new Date();
current.setHours(0, 0, 0, 0);
date_2.setHours(0, 0, 0, 0);
let d = Math.floor((current - date_2) / (1000 * 60 * 60 * 24));
if (d === 0)
return i18next.t('aujourdhuià', {time: date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"})});
else if (d === 1)
return i18next.t('hierà', {time: date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"})});
else if (d === 2)
return i18next.t('avanthierà', {time: date_.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"})});
else
return date_.toLocaleDateString();
}
function buildPouleMenu(isPoule, change_view) {
const menuDiv = document.createElement('div');
menuDiv.id = 'menu';
menuDiv.style.borderBottom = '1px solid #9EA0A1';
menuDiv.style.paddingBottom = '25px';
const ul = document.createElement('ul');
ul.id = 'onglets';
ul.style.position = 'absolute';
ul.style.border = '1px solid transparent';
ul.style.padding = '0';
ul.style.font = 'bold 11px Batang, arial, serif';
ul.style.listStyleType = 'none';
ul.style.left = '50%';
ul.style.marginTop = '0';
ul.style.width = '430px';
ul.style.marginLeft = '-215px';
function createTab(text, isActive, onClickHandler) {
const li = document.createElement('li');
if (isActive) {
li.className = 'active';
li.style.borderBottom = '1px solid #fff';
li.style.backgroundColor = '#fff';
} else {
li.style.backgroundColor = '#F4F9FD';
}
li.style.float = 'left';
li.style.height = '21px';
li.style.margin = '2px 2px 0 2px !important';
li.style.margin = '1px 2px 0 2px';
li.style.border = '1px solid #9EA0A1';
const a = document.createElement('a');
a.href = 'javascript:void(0);';
a.onclick = onClickHandler;
a.textContent = text;
a.style.display = 'block';
a.style.color = '#666';
a.style.textDecoration = 'none';
a.style.padding = '4px';
a.addEventListener('mouseover', function () {
a.style.background = '#fff';
});
a.addEventListener('mouseout', function () {
if (!isActive)
a.style.background = '#F4F9FD';
});
li.appendChild(a);
return li;
}
const li1 = createTab(i18next.t('poule'), isPoule, function () {
change_view(true);
});
ul.appendChild(li1);
const li2 = createTab(i18next.t('tournois'), !isPoule, function () {
change_view(false);
});
ul.appendChild(li2);
menuDiv.appendChild(ul);
return menuDiv;
}
function buildMatchArray(matchs) {
const pouleDiv = document.createElement('div');
let arrayContent = `
| ${i18next.t('rouge')} |
|
${i18next.t('scores')} |
|
${i18next.t('bleu')} |
${i18next.t('date')} |
`
for (const match of matchs) {
arrayContent += `
| ${match.red} |
${match.red_w ? cupImg : (match.eq ? cupImg2 : "")} |
${scoreToString(match.score)} |
${match.blue_w ? cupImg : (match.eq ? cupImg2 : "")} |
${match.blue} |
${dateToString((match.end) ? match.date : null)} |
`
}
arrayContent += `
`
pouleDiv.innerHTML = arrayContent;
return pouleDiv;
}
function buildRankArray(rankArray) {
const arrayDiv = document.createElement('div');
let arrayContent = `
| ${i18next.t('place')} |
${i18next.t('nom')} |
${i18next.t('score')} |
${i18next.t('victoire')} |
${i18next.t('ratio')} |
${i18next.t('pointsMarqués')} |
${i18next.t('pointsReçus')} |
`
for (const row of rankArray) {
arrayContent += `
| ${row.rank} |
${row.name} |
${row.score} |
${row.win} |
${row.pointRate.toFixed(3)} |
${row.pointMake} |
${row.pointTake} |
`
}
arrayContent += `
`
arrayDiv.innerHTML = arrayContent;
return arrayDiv;
}
function buildTree(treeData) {
return drawGraph(initTree(treeData))
}
function poulePage(location) {
rootDiv.innerHTML = `${i18next.t('résultatDeLaCompétition')} :
${i18next.t('back')}`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
content.innerHTML = `${i18next.t('rechercheParCatégorie')}
`;
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
let currentPoule = 0;
const loadPoule = () => {
const loading = startLoading(content);
fetch(`${apiUrlRoot}/poule/data?poule=${currentPoule}&rf=${lastRf}`)
.then(response => {
if (response.status === 204) // No content => no update
return;
response.json().then(poule => {
lastRf = (poule.genTime !== undefined) ? poule.genTime : 0;
// console.log(poule);
if (location.length === 3 && poule.type < 3) {
location.pop();
window.location.hash = location.join('/');
}
dataContainer.replaceChildren();
if (poule.type === 1) {
for (const g in poule.matchs) {
if (Object.keys(poule.matchs).length > 1) {
const text = document.createElement('h4');
text.textContent = `${i18next.t('poule')} ${g}`;
text.style.marginTop = '2em';
dataContainer.append(text);
}
dataContainer.append(buildMatchArray(poule.matchs[g]));
dataContainer.append(buildRankArray(poule.rankArray[g]));
}
} else if (poule.type === 2) {
dataContainer.append(buildTree(poule['trees']));
} else {
const change_view = (isPoule) => {
dataContainer.replaceChildren(buildPouleMenu(isPoule, change_view));
if (isPoule) {
for (const g in poule.matchs) {
if (Object.keys(poule.matchs).length > 1) {
const text = document.createElement('h4');
text.textContent = `${i18next.t('poule')} ${g}`;
text.style.marginTop = '2em';
dataContainer.append(text);
}
dataContainer.append(buildMatchArray(poule.matchs[g]));
dataContainer.append(buildRankArray(poule.rankArray[g]));
}
} else {
dataContainer.append(buildTree(poule['trees']));
}
location[2] = isPoule ? 1 : 2;
window.location.hash = location.join('/');
}
change_view((location.length === 3) ? location[2] === '1' : true);
}
})
})
.catch(e => {
console.error(e);
dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDeLaPoule')));
})
.finally(() => stopLoading(loading));
}
const loading = startLoading(content);
fetch(`${apiUrlRoot}/poule/list`)
.then(response => response.json())
.then(poule => {
const select = document.createElement('select');
select.setAttribute('id', poule.id);
select.innerHTML = ``;
for (const pouleKey of Object.keys(poule).sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()))) {
select.innerHTML += ``;
}
select.addEventListener('change', e => {
location[1] = Object.keys(poule).find(key => poule[key] === Number(e.target.value));
location[1] = encodeURI(location[1]);
window.location.hash = location.join('/');
lastRf = 0;
currentPoule = e.target.value;
loadPoule();
})
content.append(select);
content.appendChild(dataContainer);
if (location.length > 1 && location[1] !== undefined && location[1] !== '') {
const tmp = poule[decodeURI(location[1])];
select.value = tmp;
currentPoule = tmp
loadPoule();
}
})
.catch(() => rootDiv.append(new Text(i18next.t('erreurDeChargementDesCatégories'))))
.finally(() => stopLoading(loading));
rfFonction = () => {
if (currentPoule !== 0)
loadPoule();
}
rootDiv.append(content)
}
function buildCombView(comb) {
const pouleDiv = document.createElement('div');
let arrayContent = `
Info :
- ${i18next.t('nomPrénom')} : ${comb.name}
- ${i18next.t('club')} : ${comb.club}
- ${i18next.t('catégorie')} : ${comb.cat}
${i18next.t('statistique')} :
- ${i18next.t('tauxDeVictoire2', {
nb: comb.matchs.length === 0 ? "---" : (comb.totalWin / comb.matchs.filter(m => m.end).length * 100).toFixed(0),
victoires: comb.totalWin,
matchs: comb.matchs.filter(m => m.end).length
})}
- ${i18next.t('pointsMarqués2', {nb: comb.pointMake})}
- ${i18next.t('pointsReçus2', {nb: comb.pointTake})}
- ${i18next.t('ratioDuScore2', {nb: comb.pointRatio.toFixed(3)})}
${i18next.t('listeDesMatchs')}:
| ${i18next.t('date')} |
${i18next.t('poule')} |
${i18next.t('adversaire')} |
${i18next.t('scores')} |
${i18next.t('ratio')} |
|
`
for (const match of comb.matchs) {
arrayContent += `
| ${dateToString(match.date)} |
${match.poule} |
${match.adv} |
${scoreToString(match.score)} |
${match.end ? match.ratio.toFixed(3) : ""} |
${match.win ? cupImg : (match.eq ? cupImg2 : "")} |
`
}
arrayContent += `
`
pouleDiv.innerHTML = arrayContent;
return pouleDiv;
}
function combPage(location) {
rootDiv.innerHTML = `${i18next.t('résultatDeLaCompétition')} :
${i18next.t('back')}`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
content.innerHTML = `${i18next.t('rechercheParCombattant')}
`;
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
const loadComb = (id) => {
const loading = startLoading(content);
fetch(`${apiUrlRoot}/comb/data?comb=${id}`)
.then(response => response.json())
.then(comb => {
console.log(comb);
dataContainer.replaceChildren(buildCombView(comb));
})
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDuCombattant'))))
.finally(() => stopLoading(loading));
}
const loading = startLoading(content);
fetch(`${apiUrlRoot}/comb/list`)
.then(response => response.json())
.then(combs => {
const select = document.createElement('select');
select.innerHTML = ``;
for (const comb of Object.keys(combs).sort()) {
select.innerHTML += ``;
}
select.addEventListener('change', e => {
location[1] = Object.keys(combs).find(key => combs[key] === e.target.value);
location[1] = encodeURI(location[1]);
window.location.hash = location.join('/');
loadComb(e.target.value);
})
content.append(select);
content.appendChild(dataContainer);
if (location.length > 1 && location[1] !== undefined && location[1] !== '') {
const tmp = combs[decodeURI(location[1])];
select.value = tmp;
loadComb(tmp);
}
})
.catch(() => rootDiv.append(new Text(i18next.t('erreurDeChargementDesCombattants'))))
.finally(() => stopLoading(loading));
rootDiv.append(content)
}
function buildClubView(club) {
const pouleDiv = document.createElement('div');
let arrayContent = `
${i18next.t('info')} :
- ${i18next.t('nom')} : ${club.name}
- ${i18next.t('nombreDinscris')} : ${club.nb_insc}
${i18next.t('statistique')} :
- ${i18next.t('nombreDeMatchDisputé2', {nb: club.nb_match})}
- ${i18next.t('nombreDeVictoires2', {nb: club.match_w})}
- ${i18next.t('ratioDeVictoiresMoyen2', {nb: club.ratioVictoire.toFixed(3)})}
- ${i18next.t('pointsMarqués2', {nb: club.pointMake})}
- ${i18next.t('pointsReçus2', {nb: club.pointTake})}
- ${i18next.t('ratioDePointsMoyen2', {nb: club.ratioPoint.toFixed(3)})}
${i18next.t('listeDesMenbres')} :
| ${i18next.t('catégorie')} |
${i18next.t('nom')} |
${i18next.t('victoires')} |
${i18next.t('défaites')} |
${i18next.t('ratioVictoires')} |
${i18next.t('pointsMarqués')} |
${i18next.t('pointsReçus')} |
${i18next.t('ratioPoints')} |
`
for (const comb of club.combs) {
arrayContent += `
| ${comb.cat} |
${comb.name} |
${comb.w} |
${comb.l} |
${comb.ratioVictoire.toFixed(3)} |
${comb.pointMake} |
${comb.pointTake} |
${comb.ratioPoint.toFixed(3)} |
`
}
arrayContent += `
`
pouleDiv.innerHTML = arrayContent;
return pouleDiv;
}
function clubPage(location) {
rootDiv.innerHTML = `${i18next.t('résultatDeLaCompétition')} :
${i18next.t('back')}`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
content.innerHTML = `${i18next.t('rechercheParClub')}
`;
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
const loadComb = (id) => {
const loading = startLoading(content);
fetch(`${apiUrlRoot}/club/data?club=${id}`)
.then(response => response.json())
.then(club => {
console.log(club);
dataContainer.replaceChildren(buildClubView(club));
})
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDuClub'))))
.finally(() => stopLoading(loading));
}
const loading = startLoading(content);
fetch(`${apiUrlRoot}/club/list`)
.then(response => response.json())
.then(clubs => {
const select = document.createElement('select');
select.innerHTML = ``;
for (const club of Object.keys(clubs).sort()) {
select.innerHTML += ``;
}
select.addEventListener('change', e => {
if (e.target.value === '0')
return;
location[1] = Object.keys(clubs).find(key => clubs[key] === Number(e.target.value));
location[1] = encodeURI(location[1]);
window.location.hash = location.join('/');
loadComb(e.target.value);
})
content.append(select);
content.appendChild(dataContainer);
if (location.length > 1 && location[1] !== undefined && location[1] !== '') {
const tmp = clubs[decodeURI(location[1])];
select.value = tmp;
loadComb(tmp);
}
})
.catch(() => rootDiv.append(new Text(i18next.t('erreurDeChargementDesClubs'))))
.finally(() => stopLoading(loading));
rootDiv.append(content)
}
function buildCombsView(combs) {
const pouleDiv = document.createElement('div');
let arrayContent = `
${i18next.t('statistique')} :
- ${i18next.t('nombreDinscris2', {nb: combs.nb_insc})}
- ${i18next.t('nombreDeMatchDisputé2', {nb: combs.tt_match})}
- ${i18next.t('pointsMarqués2', {nb: combs.point})}
${i18next.t('listeDesCombattants')} :
| ${i18next.t('catégorie')} |
${i18next.t('club')} |
${i18next.t('nom')} |
${i18next.t('victoires')} |
${i18next.t('défaites')} |
${i18next.t('ratioVictoires')} |
${i18next.t('pointsMarqués')} |
${i18next.t('pointsReçus')} |
${i18next.t('ratiosPoints')} |
`
for (const comb of combs.combs) {
arrayContent += `
| ${comb.cat} |
${comb.club} |
${comb.name} |
${comb.w} |
${comb.l} |
${comb.ratioVictoire.toFixed(3)} |
${comb.pointMake} |
${comb.pointTake} |
${comb.ratioPoint.toFixed(3)} |
`
}
arrayContent += `
`
pouleDiv.innerHTML = arrayContent;
return pouleDiv;
}
function combsPage() {
rootDiv.innerHTML = `${i18next.t('résultatDeLaCompétition')} :
${i18next.t('back')}`;
document.getElementById('homeLink').addEventListener('click', () => setSubPage('home'));
const content = document.createElement('div');
content.style.marginTop = '1em';
const dataContainer = document.createElement('div');
dataContainer.id = 'data-container';
const loading = startLoading(content);
fetch(`${apiUrlRoot}/comb/get_all`)
.then(response => response.json())
.then(combs => {
console.log(combs);
dataContainer.replaceChildren(buildCombsView(combs));
})
.catch(() => dataContainer.replaceChildren(new Text(i18next.t('erreurDeChargementDeLaListe'))))
.finally(() => stopLoading(loading));
content.append(dataContainer);
rootDiv.append(content)
}
export async function initCompetitionApi(apiUrlRoot_, host) {
apiUrlRoot = apiUrlRoot_;
const options = {
order: ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag'],
caches: [],
}
const backend = {
loadPath: `${host}/locales/{{lng}}/{{ns}}.json`,
}
await i18next
.use(i18nextHttpBackend)
.use(i18nextBrowserLanguagedetector)
.init({
supportedLngs: ['fr', 'en'],
fallbackLng: 'fr',
debug: host.startsWith('http://localhost'),
interpolation: {
escapeValue: true,
},
detection: options,
backend: backend,
ns: ['result'],
defaultNS: 'result',
})
console.log("apiUrlRoot:", apiUrlRoot)
console.log("FFSAF Competition API initialized.")
let hash = window.location.hash.substring(1);
if (hash.length === 0)
setSubPage('home');
else
setSubPage(hash);
}
class TreeNode {
constructor(data) {
this.data = data;
this.left = null;
this.right = null;
}
death() {
let dg = 0;
let dd = 0;
if (this.right != null)
dg = this.right.death();
if (this.left != null)
dg = this.left.death();
return 1 + Math.max(dg, dd);
}
}
function initTree(data_in) {
let out = [];
for (const din of data_in) {
out.push(parseTree(din));
}
return out;
}
function parseTree(data_in) {
if (data_in?.data == null)
return null;
let node = new TreeNode(data_in.data);
node.left = parseTree(data_in?.left);
node.right = parseTree(data_in?.right);
return node;
}
const max_x = 500;
const size = 24;
function getBounds(root) {
let px = max_x;
let py;
let maxx, minx, miny, maxy
function drawNode(tree, px, py) {
let death = tree.death() - 1
if (death === 0) {
if (miny > py - size - ((size * 1.5 / 2) | 0)) miny = py - size - (size * 1.5 / 2) | 0;
if (maxy < py + size + ((size * 1.5 / 2) | 0)) maxy = py + size + (size * 1.5 / 2) | 0;
} else {
if (miny > py - size * 2 * death - ((size * 1.5 / 2) | 0))
miny = py - size * 2 * death - ((size * 1.5 / 2) | 0);
if (maxy < py + size * 2 * death + ((size * 1.5 / 2) | 0))
maxy = py + size * 2 * death + ((size * 1.5 / 2) | 0);
}
if (minx > px - size * 2 - size * 8) minx = px - size * 2 - size * 8;
if (tree.left != null) drawNode(tree.left, px - size * 2 - size * 8, py - size * 2 * death);
if (tree.right != null) drawNode(tree.right, px - size * 2 - size * 8, py + size * 2 * death);
}
if (root != null) {
py = (size * 2 * root.at(0).death() + (((size * 1.5 / 2) | 0) + size) * root.at(0).death()) * 2;
maxx = px;
minx = px;
miny = py - (size * 1.5 / 2) | 0;
maxy = py + (size * 1.5 / 2) | 0;
for (const node of root) {
px = px - size * 2 - size * 8;
if (minx > px) minx = px;
drawNode(node, px, py);
//graphics2D.drawRect(minx, miny, maxx - minx, maxy - miny);
py = maxy + ((size * 2 * node.death() + ((size * 1.5 / 2) | 0)));
px = maxx;
}
} else {
minx = 0;
maxx = 0;
miny = 0;
maxy = 0;
}
return [minx, maxx, miny, maxy];
}
function drawGraph(root = []) {
const canvas = document.createElement('canvas');
canvas.id = "myCanvas";
canvas.style.border = "1px solid grey";
canvas.style.marginTop = "10px";
const ctx = canvas.getContext("2d");
const [minx, maxx, miny, maxy] = getBounds(root);
canvas.width = maxx - minx;
canvas.height = maxy - miny;
ctx.translate(-minx, -miny);
ctx.fillStyle = "#000000"
ctx.lineWidth = 2
ctx.strokeStyle = "#000000"
function printText(s, x, y, width, height, lineG, lineD) {
ctx.save();
ctx.translate(x, y);
let tSize = 17;
let ratioX = height * 1. / 20.;
ctx.font = "100 " + tSize + "px Arial";
let mw = width - (ratioX * 2) | 0;
if (ctx.measureText(s).width > mw) {
let dTextSize = true;
do {
tSize--;
ctx.font = tSize + "px Arial";
} while (ctx.measureText(s).width > mw && tSize > 10)
if (!dTextSize || ctx.measureText(s).width > mw) {
let s = "";
for (const string2 in s.split(" ")) {
if (ctx.measureText(s + string2).width >= mw) {
s += "...";
break;
} else {
s += string2 + " "
}
}
}
}
const text = ctx.measureText(s)
let dx = (width - text.width) / 2;
let dy = ((height - text.actualBoundingBoxDescent) / 2) + (text.actualBoundingBoxAscent / 2);
ctx.fillText(s, dx, dy, width - dy);
ctx.restore();
ctx.beginPath();
if (lineD) {
ctx.moveTo((ratioX * 2.5 + x + dx + text.width) | 0, y + height / 2);
ctx.lineTo(x + width, y + height / 2)
}
if (lineG) {
ctx.moveTo(x, y + height / 2);
ctx.lineTo((dx + x - ratioX * 2.5) | 0, y + height / 2)
}
ctx.stroke();
}
function printScores(scores, px, py, scale) {
ctx.save();
ctx.translate(px - size * 2, py - size * scale);
ctx.font = "100 " + 14 + "px Arial";
ctx.textBaseline = 'top';
for (let i = 0; i < scores.length; i++) {
const score = scorePrint(scores[i].s1) + "-" + scorePrint(scores[i].s2);
const div = (scores.length <= 2) ? 2 : (scores.length >= 4) ? 4 : 3;
const text = ctx.measureText(score);
let dx = (size * 2 - text.width) / 2;
let dy = ((size * 2 / div - text.actualBoundingBoxDescent) / 2) + (text.actualBoundingBoxAscent / 2);
ctx.fillStyle = '#ffffffdd';
ctx.fillRect(dx, size * 2 * scale / div * (i) + dy, text.width, 14);
ctx.fillStyle = "#000000";
ctx.fillText(score, dx, size * 2 * scale / div * (i) + dy, size * 2);
}
ctx.restore();
}
function drawNode(tree, px, py) {
ctx.beginPath()
ctx.moveTo(px, py)
ctx.lineTo(px - size, py)
ctx.stroke();
let death = tree.death() - 1
let match = tree.data
if (death === 0) {
ctx.beginPath()
ctx.moveTo(px - size, py + size)
ctx.lineTo(px - size, py - size)
ctx.moveTo(px - size, py + size)
ctx.lineTo(px - size * 2, py + size)
ctx.moveTo(px - size, py - size)
ctx.lineTo(px - size * 2, py - size)
ctx.stroke();
printScores(match.scores, px, py, 1);
ctx.fillStyle = "#FF0000";
printText((match.c1FullName == null) ? "" : match.c1FullName, px - size * 2 - size * 8, py - size - (size * 1.5 / 2) | 0,
size * 8, (size * 1.5) | 0, false, true)
ctx.fillStyle = "#0000FF";
printText((match.c2FullName == null) ? "" : match.c2FullName, px - size * 2 - size * 8, py + size - (size * 1.5 / 2) | 0,
size * 8, (size * 1.5) | 0, false, true)
if (max_y < py + size + ((size * 1.5 / 2) | 0)) max_y = py + size + (size * 1.5 / 2) | 0;
} else {
ctx.beginPath()
ctx.moveTo(px - size, py)
ctx.lineTo(px - size, py + size * 2 * death)
ctx.moveTo(px - size, py)
ctx.lineTo(px - size, py - size * 2 * death)
ctx.moveTo(px - size, py + size * 2 * death)
ctx.lineTo(px - size * 2, py + size * 2 * death)
ctx.moveTo(px - size, py - size * 2 * death)
ctx.lineTo(px - size * 2, py - size * 2 * death)
ctx.stroke();
printScores(match.scores, px, py, 1.5);
ctx.fillStyle = "#FF0000";
printText((match.c1FullName == null) ? "" : match.c1FullName, px - size * 2 - size * 8, py - size * 2 * death - (size * 1.5 / 2) | 0,
size * 8, (size * 1.5) | 0, true, true)
ctx.fillStyle = "#0000FF";
printText((match.c2FullName == null) ? "" : match.c2FullName, px - size * 2 - size * 8, py + size * 2 * death - (size * 1.5 / 2) | 0,
size * 8, (size * 1.5) | 0, true, true)
if (max_y < py + size * 2 * death + ((size * 1.5 / 2) | 0))
max_y = py + size * 2 * death + ((size * 1.5 / 2) | 0);
}
ctx.stroke();
if (tree.left != null) drawNode(tree.left, px - size * 2 - size * 8, py - size * 2 * death);
if (tree.right != null) drawNode(tree.right, px - size * 2 - size * 8, py + size * 2 * death);
}
let px = max_x;
let py;
let max_y
if (root != null) {
py = (size * 2 * root.at(0).death() + (((size * 1.5 / 2) | 0) + size) * root.at(0).death()) * 2;
max_y = py + (size * 1.5 / 2) | 0;
for (const node of root) {
let win_name = "";
if (node.data.end) {
if (node.data.win > 0)
win_name = (node.data.c1FullName === null) ? "???" : node.data.c1FullName;
else
win_name = (node.data.c2FullName === null) ? "???" : node.data.c2FullName;
}
ctx.fillStyle = "#18A918";
printText(win_name, px - size * 2 - size * 8, py - ((size * 1.5 / 2) | 0),
size * 8, (size * 1.5) | 0, true, false);
px = px - size * 2 - size * 8;
drawNode(node, px, py);
py = max_y + ((size * 2 * node.death() + ((size * 1.5 / 2) | 0)));
px = max_x;
}
}
return canvas;
}