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 = `
` for (const match of matchs) { arrayContent += ` ` } arrayContent += `
${i18next.t('rouge')} ${i18next.t('scores')} ${i18next.t('bleu')} ${i18next.t('date')}
${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)}
` pouleDiv.innerHTML = arrayContent; return pouleDiv; } function buildRankArray(rankArray) { const arrayDiv = document.createElement('div'); let arrayContent = `
` for (const row of rankArray) { arrayContent += ` ` } arrayContent += `
${i18next.t('place')} ${i18next.t('nom')} ${i18next.t('score')} ${i18next.t('victoire')} ${i18next.t('ratio')} ${i18next.t('pointsMarqués')} ${i18next.t('pointsReçus')}
${row.rank} ${row.name} ${row.score} ${row.win} ${row.pointRate.toFixed(3)} ${row.pointMake} ${row.pointTake}
` 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('statistique')} :

${i18next.t('listeDesMatchs')}:

` for (const match of comb.matchs) { arrayContent += ` ` } arrayContent += `
${i18next.t('date')} ${i18next.t('poule')} ${i18next.t('adversaire')} ${i18next.t('scores')} ${i18next.t('ratio')}
${dateToString(match.date)} ${match.poule} ${match.adv} ${scoreToString(match.score)} ${match.end ? match.ratio.toFixed(3) : ""} ${match.win ? cupImg : (match.eq ? cupImg2 : "")}
` 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('statistique')} :

${i18next.t('listeDesMenbres')} :

` for (const comb of club.combs) { arrayContent += ` ` } arrayContent += `
${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')}
${comb.cat} ${comb.name} ${comb.w} ${comb.l} ${comb.ratioVictoire.toFixed(3)} ${comb.pointMake} ${comb.pointTake} ${comb.ratioPoint.toFixed(3)}
` 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('listeDesCombattants')} :

` for (const comb of combs.combs) { arrayContent += ` ` } arrayContent += `
${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')}
${comb.cat} ${comb.club} ${comb.name} ${comb.w} ${comb.l} ${comb.ratioVictoire.toFixed(3)} ${comb.pointMake} ${comb.pointTake} ${comb.ratioPoint.toFixed(3)}
` 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; }