You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
555 lines
21 KiB
555 lines
21 KiB
function renderBracket(options) {
|
|
const bracket = document.getElementById("bracket");
|
|
const matchTemplates = document.getElementById("match-templates").children;
|
|
const rounds = [];
|
|
const matchPositions = [];
|
|
const matchDisabled = [];
|
|
const doubleButterflyMode = options.doubleButterflyMode;
|
|
const displayLoserFinal = options.displayLoserFinal;
|
|
const tournamentId = options.tournamentId;
|
|
const isBroadcast = options.isBroadcast;
|
|
// Group matches by round
|
|
Array.from(matchTemplates).forEach((template) => {
|
|
const roundIndex = parseInt(template.dataset.matchRound);
|
|
if (!rounds[roundIndex]) {
|
|
rounds[roundIndex] = [];
|
|
}
|
|
rounds[roundIndex].push(template);
|
|
});
|
|
|
|
// First create a test match to get natural height
|
|
const firstMatch = document.createElement("div");
|
|
firstMatch.className = "butterfly-match";
|
|
firstMatch.innerHTML = `<div class="match-content">${rounds[0][0].innerHTML}</div>`;
|
|
bracket.appendChild(firstMatch);
|
|
const matchHeight = firstMatch.offsetHeight;
|
|
const matchSpacing = 10;
|
|
const baseDistance = matchHeight + matchSpacing;
|
|
bracket.innerHTML = "";
|
|
const roundCount = rounds.length;
|
|
let finalRoundIndex = roundCount - 1;
|
|
if (doubleButterflyMode == true) {
|
|
finalRoundIndex = finalRoundIndex / 2;
|
|
}
|
|
let nextMatchDistance = baseDistance;
|
|
let minimumMatchDistance = 1;
|
|
|
|
const screenWidth = window.innerWidth;
|
|
let roundTotalCount = roundCount;
|
|
let initialPadding = 40;
|
|
if (doubleButterflyMode == true && roundCount > 4) {
|
|
roundTotalCount = roundCount - 1;
|
|
initialPadding = 46;
|
|
}
|
|
const padding = initialPadding * roundTotalCount; // Account for some padding/margin
|
|
const availableWidth = screenWidth - padding;
|
|
let responsiveMatchWidth = Math.min(
|
|
365,
|
|
Math.max(365, Math.floor(availableWidth / roundTotalCount)),
|
|
);
|
|
|
|
let topMargin = 0;
|
|
if (isBroadcast) {
|
|
responsiveMatchWidth = Math.floor(availableWidth / roundTotalCount);
|
|
let screenHeight = window.innerHeight;
|
|
if (roundTotalCount <= 1) {
|
|
topMargin = 240;
|
|
} else if (roundTotalCount == 2) {
|
|
topMargin = 180;
|
|
} else if (roundTotalCount == 3) {
|
|
topMargin = 120;
|
|
} else if (roundTotalCount == 4) {
|
|
topMargin = 60;
|
|
} else if (roundTotalCount == 5) {
|
|
topMargin = 40;
|
|
} else if (roundTotalCount == 6) {
|
|
topMargin = -20;
|
|
} else {
|
|
topMargin = 0;
|
|
}
|
|
|
|
if (screenHeight <= 720) {
|
|
} else if (screenHeight <= 1200) {
|
|
if (topMargin <= 0) {
|
|
topMargin = 40;
|
|
}
|
|
topMargin = topMargin * 2;
|
|
} else {
|
|
if (topMargin <= 0) {
|
|
topMargin = 120;
|
|
}
|
|
topMargin = topMargin * 3;
|
|
}
|
|
}
|
|
|
|
rounds.forEach((roundMatches, roundIndex) => {
|
|
if (rounds[0].length <= 2 && doubleButterflyMode) {
|
|
minimumMatchDistance = 2;
|
|
nextMatchDistance = baseDistance * 2;
|
|
}
|
|
const roundDiv = document.createElement("div");
|
|
roundDiv.className = "butterfly-round";
|
|
roundDiv.style.setProperty("--match-width", `${responsiveMatchWidth}px`);
|
|
if (doubleButterflyMode == true && roundCount > 3) {
|
|
if (roundIndex >= finalRoundIndex - 1) {
|
|
roundDiv.style.transform = `translateX(-50%)`;
|
|
if (roundIndex >= finalRoundIndex + 2) {
|
|
roundDiv.style.transform = `translateX(-100%)`;
|
|
}
|
|
}
|
|
}
|
|
// Create title
|
|
const titleDiv = document.createElement("div");
|
|
titleDiv.className = "round-title";
|
|
if (isBroadcast) {
|
|
titleDiv.className = "round-title broadcast-mode";
|
|
}
|
|
// Get the match group name and format
|
|
const firstMatchTemplate = roundMatches[0].closest(".match-template");
|
|
const matchGroupName = firstMatchTemplate.dataset.matchGroupName;
|
|
const matchFormat = firstMatchTemplate.dataset.matchFormat;
|
|
const roundId = firstMatchTemplate.dataset.roundId; // Add this line
|
|
const realRoundIndex = firstMatchTemplate.dataset.roundIndex; // Add this line
|
|
|
|
let nameSpan = document.createElement("div");
|
|
nameSpan.className = "round-name";
|
|
nameSpan.textContent = matchGroupName;
|
|
if (
|
|
roundIndex == finalRoundIndex ||
|
|
(roundIndex == finalRoundIndex - 1 && displayLoserFinal) ||
|
|
(roundIndex == finalRoundIndex + 1 && displayLoserFinal) ||
|
|
isBroadcast
|
|
) {
|
|
} else {
|
|
nameSpan = document.createElement("a");
|
|
nameSpan.className = "round-name";
|
|
nameSpan.classList.add("button");
|
|
nameSpan.textContent = matchGroupName;
|
|
if (roundId) {
|
|
nameSpan.href = `/tournament/${tournamentId}/round/${roundId}/bracket/`;
|
|
nameSpan.style.cursor = "pointer";
|
|
}
|
|
}
|
|
|
|
const formatSpan = document.createElement("div");
|
|
formatSpan.className = "round-format";
|
|
formatSpan.textContent = matchFormat;
|
|
|
|
titleDiv.appendChild(nameSpan);
|
|
titleDiv.appendChild(formatSpan);
|
|
|
|
// Create matches container
|
|
const matchesContainer = document.createElement("div");
|
|
matchesContainer.className = "matches-container";
|
|
roundDiv.appendChild(matchesContainer);
|
|
if (matchPositions[roundIndex] == undefined) {
|
|
matchPositions[roundIndex] = {};
|
|
}
|
|
matchDisabled[roundIndex] = []; // Initialize array for this round
|
|
roundMatches.forEach((matchTemplate, matchIndex) => {
|
|
const matchTitle = matchTemplate.dataset.matchTitle;
|
|
const matchRealIndex = matchTemplate.dataset.matchRealIndex;
|
|
const matchDiv = document.createElement("div");
|
|
matchDiv.className = "butterfly-match";
|
|
|
|
matchDiv.style.position = "absolute";
|
|
const isDisabled = matchTemplate.dataset.disabled === "true";
|
|
matchDisabled[roundIndex][matchRealIndex] = isDisabled;
|
|
let isIncomingLineIsDisabled = isDisabled;
|
|
let isOutgoingLineIsDisabled = isDisabled;
|
|
let top;
|
|
const currentMatchesCount = roundMatches.length;
|
|
if (roundIndex > finalRoundIndex) {
|
|
matchDiv.classList.add("reverse-bracket");
|
|
|
|
if (roundIndex <= finalRoundIndex + 2) {
|
|
const values = Object.values(
|
|
matchPositions[roundCount - roundIndex - 1],
|
|
);
|
|
top = values[matchIndex];
|
|
} else {
|
|
top = matchPositions[roundIndex][matchRealIndex];
|
|
console.log(matchTitle, top);
|
|
}
|
|
}
|
|
|
|
if (roundIndex === 0) {
|
|
if (doubleButterflyMode == false) {
|
|
nextMatchDistance = 0;
|
|
} else {
|
|
if (realRoundIndex > 1) {
|
|
nextMatchDistance = 0;
|
|
}
|
|
}
|
|
if (roundCount > 1) {
|
|
const nextMatchesCount = rounds[roundIndex + 1].length;
|
|
|
|
if (currentMatchesCount == nextMatchesCount && roundCount > 2) {
|
|
nextMatchDistance = 0;
|
|
}
|
|
}
|
|
|
|
top = matchIndex * (matchHeight + matchSpacing) * minimumMatchDistance;
|
|
|
|
if (roundCount == 3 && doubleButterflyMode) {
|
|
top = top + (matchHeight + matchSpacing) / 2;
|
|
}
|
|
} else if (roundIndex === roundCount - 1 && doubleButterflyMode == true) {
|
|
if (roundCount > 3) {
|
|
nextMatchDistance = 0;
|
|
} else {
|
|
nextMatchDistance = nextMatchDistance / 2;
|
|
}
|
|
} else if (roundIndex == finalRoundIndex && realRoundIndex == 0) {
|
|
//realRoundIndex 0 means final's round
|
|
const values = Object.values(matchPositions[roundIndex - 1]);
|
|
const parentPos1 = values[0];
|
|
const parentPos2 = values[1];
|
|
|
|
if (doubleButterflyMode == true) {
|
|
let lgth = matchPositions[0].length / 2;
|
|
let index = lgth + matchIndex - 1;
|
|
// If index goes negative, use 0 instead
|
|
if (displayLoserFinal == true) {
|
|
if (matchIndex == 0) {
|
|
top = parentPos1 - baseDistance / 2;
|
|
} else {
|
|
top = parentPos1 + baseDistance / 2;
|
|
}
|
|
nextMatchDistance = 0;
|
|
} else {
|
|
top = parentPos1;
|
|
nextMatchDistance = 0;
|
|
}
|
|
} else {
|
|
top = (parentPos1 + parentPos2) / 2;
|
|
if (matchIndex == 0) {
|
|
nextMatchDistance = parentPos2 - parentPos1;
|
|
} else {
|
|
nextMatchDistance = 0;
|
|
}
|
|
|
|
if (displayLoserFinal == true) {
|
|
if (matchIndex == 1) {
|
|
top = matchPositions[roundIndex][0] + baseDistance + 80;
|
|
isIncomingLineIsDisabled = true;
|
|
}
|
|
}
|
|
}
|
|
} else if (
|
|
(roundIndex == finalRoundIndex && realRoundIndex != 0) ||
|
|
roundIndex < finalRoundIndex
|
|
) {
|
|
const parentIndex1 = matchRealIndex * 2 + 1;
|
|
const parentIndex2 = matchRealIndex * 2 + 2;
|
|
const parentPos1 = matchPositions[roundIndex - 1][parentIndex1];
|
|
const parentPos2 = matchPositions[roundIndex - 1][parentIndex2];
|
|
const parent1Disable = matchDisabled[roundIndex - 1][parentIndex1];
|
|
const parent2Disable = matchDisabled[roundIndex - 1][parentIndex2];
|
|
if (
|
|
(parent1Disable == undefined || parent1Disable == true) &&
|
|
(parent2Disable == undefined || parent2Disable == true)
|
|
) {
|
|
isIncomingLineIsDisabled = true;
|
|
}
|
|
if (
|
|
matchPositions[roundIndex - 1][parentIndex1] != undefined &&
|
|
matchPositions[roundIndex - 1][parentIndex2] != undefined
|
|
) {
|
|
top = (parentPos1 + parentPos2) / 2;
|
|
if (parent1Disable && parent2Disable) {
|
|
nextMatchDistance = 0;
|
|
const keys = Object.keys(matchPositions[roundIndex]).map(Number);
|
|
const lastKey = Math.max(...keys);
|
|
top =
|
|
(matchHeight + matchSpacing) * minimumMatchDistance * keys.length;
|
|
} else {
|
|
nextMatchDistance = parentPos2 - parentPos1;
|
|
}
|
|
} else if (matchPositions[roundIndex - 1][parentIndex1] != undefined) {
|
|
nextMatchDistance = 0;
|
|
top = matchPositions[roundIndex - 1][parentIndex1];
|
|
} else if (matchPositions[roundIndex - 1][parentIndex2] != undefined) {
|
|
nextMatchDistance = 0;
|
|
top = matchPositions[roundIndex - 1][parentIndex2];
|
|
} else {
|
|
nextMatchDistance = 0;
|
|
top = 0;
|
|
}
|
|
} else if (roundIndex < roundCount) {
|
|
const parentIndex1 = matchRealIndex * 2 + 1;
|
|
const parentIndex2 = matchRealIndex * 2 + 2;
|
|
const parentMatch1 = rounds[roundIndex + 1].find(
|
|
(match) => parseInt(match.dataset.matchRealIndex) === parentIndex1,
|
|
);
|
|
const parentMatch2 = rounds[roundIndex + 1].find(
|
|
(match) => parseInt(match.dataset.matchRealIndex) === parentIndex2,
|
|
);
|
|
|
|
if (matchPositions[roundIndex + 1] == undefined) {
|
|
matchPositions[roundIndex + 1] = {};
|
|
}
|
|
if (
|
|
parentMatch1 != undefined &&
|
|
parentMatch2 != undefined &&
|
|
parentMatch1.dataset.disabled == "false" &&
|
|
parentMatch2.dataset.disabled == "false"
|
|
) {
|
|
console.log(
|
|
roundIndex,
|
|
matchTitle,
|
|
parentMatch1.dataset.matchTitle,
|
|
parentMatch2.dataset.matchTitle,
|
|
parentMatch1.dataset.disabled,
|
|
parentMatch2.dataset.disabled,
|
|
top,
|
|
);
|
|
nextMatchDistance = baseDistance;
|
|
matchPositions[roundIndex + 1][parentIndex1] = top - baseDistance / 2;
|
|
matchPositions[roundIndex + 1][parentIndex2] = top + baseDistance / 2;
|
|
console.log(matchPositions[roundIndex + 1]);
|
|
// } else if (parentMatch1 != undefined) {
|
|
// matchPositions[roundIndex + 1][parentIndex1] = top;
|
|
// nextMatchDistance = 0;
|
|
// } else if (parentMatch2 != undefined) {
|
|
// matchPositions[roundIndex + 1][parentIndex1] = top;
|
|
// nextMatchDistance = 0;
|
|
} else if (
|
|
parentMatch2 != undefined &&
|
|
parentMatch2.dataset.disabled == "false"
|
|
) {
|
|
nextMatchDistance = 0;
|
|
matchPositions[roundIndex + 1][parentIndex2] = top;
|
|
} else if (
|
|
parentMatch1 != undefined &&
|
|
parentMatch1.dataset.disabled == "false"
|
|
) {
|
|
nextMatchDistance = 0;
|
|
matchPositions[roundIndex + 1][parentIndex1] = top;
|
|
} else {
|
|
isOutgoingLineIsDisabled = true;
|
|
}
|
|
}
|
|
|
|
if (doubleButterflyMode == true) {
|
|
if (roundIndex >= finalRoundIndex - 2) {
|
|
if (roundIndex == finalRoundIndex - 1) {
|
|
matchDiv.classList.add("reverse-bracket");
|
|
isIncomingLineIsDisabled = true;
|
|
nextMatchDistance = nextMatchDistance / 2;
|
|
}
|
|
if (roundIndex == finalRoundIndex + 1) {
|
|
matchDiv.classList.remove("reverse-bracket");
|
|
isOutgoingLineIsDisabled = true;
|
|
nextMatchDistance = nextMatchDistance;
|
|
}
|
|
}
|
|
}
|
|
matchDiv.style.setProperty(
|
|
"--semi-final-distance",
|
|
`${baseDistance / 2}px`,
|
|
);
|
|
|
|
matchDiv.style.setProperty(
|
|
"--next-match-distance",
|
|
`${nextMatchDistance}px`,
|
|
);
|
|
|
|
let roundTopMargin = 80;
|
|
if (doubleButterflyMode) {
|
|
if (window.innerHeight <= 720) {
|
|
roundTopMargin = 50;
|
|
} else if (window.innerHeight > 720 && window.innerHeight <= 1200) {
|
|
roundTopMargin = 80;
|
|
} else if (window.innerHeight > 1200) {
|
|
roundTopMargin = 140;
|
|
}
|
|
}
|
|
matchDiv.style.top = `${top + topMargin}px`;
|
|
matchPositions[roundIndex][matchRealIndex] = top;
|
|
|
|
if (matchIndex === 0) {
|
|
// // Add logo for final round
|
|
// if (roundIndex == finalRoundIndex) {
|
|
// const logoDiv = document.createElement('div');
|
|
// logoDiv.className = 'round-logo';
|
|
// const logoImg = document.createElement('img');
|
|
// logoImg.src = '/static/tournaments/images/PadelClub_logo_512.png';
|
|
// logoImg.alt = 'PadelClub Logo';
|
|
// logoDiv.appendChild(logoImg);
|
|
// logoDiv.style.transform = `translateX(-50%)`;
|
|
// matchesContainer.appendChild(logoDiv);
|
|
// }
|
|
|
|
// Position title above the first match
|
|
titleDiv.style.top = `${topMargin - roundTopMargin}px`; // Adjust the 60px offset as needed
|
|
if (
|
|
(roundIndex == finalRoundIndex && realRoundIndex == 0) ||
|
|
isBroadcast == true
|
|
) {
|
|
titleDiv.style.top = `${top + topMargin - roundTopMargin}px`; // Adjust the 60px offset as needed
|
|
}
|
|
titleDiv.style.position = "absolute";
|
|
if (roundCount >= 5 && doubleButterflyMode == true) {
|
|
let offset = 40;
|
|
if (window.innerHeight <= 720) {
|
|
offset = 40;
|
|
} else if (window.innerHeight <= 1200) {
|
|
offset = 60;
|
|
} else {
|
|
offset = 120;
|
|
}
|
|
if (roundIndex == finalRoundIndex - 1) {
|
|
titleDiv.style.marginLeft = `${offset}px`;
|
|
} else if (roundIndex == finalRoundIndex + 1) {
|
|
titleDiv.style.marginLeft = `-${offset}px`;
|
|
}
|
|
}
|
|
matchesContainer.appendChild(titleDiv);
|
|
}
|
|
|
|
if (
|
|
roundIndex == finalRoundIndex &&
|
|
realRoundIndex == 0 &&
|
|
matchIndex === 1 &&
|
|
matchDisabled[roundIndex][matchRealIndex] == false &&
|
|
displayLoserFinal == true &&
|
|
doubleButterflyMode == false
|
|
) {
|
|
let nameSpan = document.createElement("div");
|
|
nameSpan.className = "round-name";
|
|
nameSpan.textContent = matchTitle;
|
|
const formatSpan = document.createElement("div");
|
|
formatSpan.className = "round-format";
|
|
formatSpan.textContent = matchFormat;
|
|
const titleDiv = document.createElement("div");
|
|
titleDiv.className = "round-title";
|
|
titleDiv.appendChild(nameSpan);
|
|
titleDiv.appendChild(formatSpan);
|
|
titleDiv.style.top = `${top + topMargin - 80}px`; // Adjust the 60px offset as needed
|
|
titleDiv.style.position = "absolute";
|
|
matchesContainer.appendChild(titleDiv);
|
|
}
|
|
|
|
if (roundIndex == 0 && roundCount > 3) {
|
|
isIncomingLineIsDisabled = true;
|
|
} else if (roundIndex == 0 && roundCount < 4) {
|
|
isIncomingLineIsDisabled = false;
|
|
}
|
|
matchDiv.innerHTML = `
|
|
<div class="incoming-line ${isIncomingLineIsDisabled ? "disabled" : ""}"></div>
|
|
<div class="match-content ${isDisabled ? "disabled" : ""}">${matchTemplate.innerHTML}</div>
|
|
<div class="outgoing-line ${isOutgoingLineIsDisabled ? "disabled" : ""}"></div>
|
|
`;
|
|
|
|
if (doubleButterflyMode == true) {
|
|
if (roundIndex >= finalRoundIndex - 2) {
|
|
if (roundIndex === finalRoundIndex - 2) {
|
|
if (matchIndex === 0) {
|
|
const outgoingLine = document.createElement("div");
|
|
outgoingLine.className = "outgoing-line-downward";
|
|
matchDiv.appendChild(outgoingLine);
|
|
} else {
|
|
const outgoingLine = document.createElement("div");
|
|
outgoingLine.className = "outgoing-line-upward";
|
|
matchDiv.appendChild(outgoingLine);
|
|
}
|
|
}
|
|
if (roundIndex === finalRoundIndex + 2) {
|
|
if (matchIndex === 0) {
|
|
const outgoingLine = document.createElement("div");
|
|
outgoingLine.className = "outgoing-line-downward";
|
|
matchDiv.appendChild(outgoingLine);
|
|
} else {
|
|
const outgoingLine = document.createElement("div");
|
|
outgoingLine.className = "outgoing-line-upward";
|
|
matchDiv.appendChild(outgoingLine);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if (
|
|
// roundIndex == finalRoundIndex - 1 &&
|
|
// displayLoserFinal == true &&
|
|
// doubleButterflyMode == true
|
|
// ) {
|
|
// const matchDiv2 = document.createElement("div");
|
|
// matchDiv2.className = "butterfly-match";
|
|
// matchDiv2.classList.add("inward");
|
|
// matchDiv2.classList.add("semi-final");
|
|
// matchDiv2.style.setProperty(
|
|
// "--next-match-distance",
|
|
// `${baseDistance}px`,
|
|
// );
|
|
// matchDiv2.style.top = `${top}px`;
|
|
// matchDiv2.innerHTML = `<div class="match-content">${rounds[0][0].innerHTML}</div>`;
|
|
// matchesContainer.appendChild(matchDiv2); // Append to matchesContainer instead of roundDiv
|
|
// }
|
|
matchesContainer.appendChild(matchDiv); // Append to matchesContainer instead of roundDiv
|
|
});
|
|
|
|
bracket.appendChild(roundDiv);
|
|
});
|
|
|
|
if (isBroadcast == false || isBroadcast == undefined) {
|
|
setTimeout(() => {
|
|
const roundDivs = document.querySelectorAll(".butterfly-round");
|
|
|
|
// First, find the maximum bottom position across all rounds
|
|
let globalMaxBottom = 0;
|
|
|
|
roundDivs.forEach((roundDiv) => {
|
|
const matches = roundDiv.querySelectorAll(".butterfly-match");
|
|
matches.forEach((match) => {
|
|
const bottom = match.offsetTop + match.offsetHeight;
|
|
if (bottom > globalMaxBottom) {
|
|
globalMaxBottom = bottom;
|
|
}
|
|
});
|
|
});
|
|
|
|
// Now create and position footers for all rounds at the same y-position
|
|
roundDivs.forEach((roundDiv, index) => {
|
|
// Get the match templates from this round to extract data
|
|
const roundMatches = rounds[index] || [];
|
|
if (roundMatches.length > 0) {
|
|
const firstMatchTemplate = roundMatches[0].closest(".match-template");
|
|
const roundId = firstMatchTemplate.dataset.roundId;
|
|
const realRoundIndex = firstMatchTemplate.dataset.roundIndex;
|
|
if (realRoundIndex > 1) {
|
|
// Create footer div
|
|
const footerDiv = document.createElement("div");
|
|
footerDiv.className = "round-footer";
|
|
footerDiv.style.width = `${responsiveMatchWidth}px`;
|
|
footerDiv.style.paddingBottom = "40px";
|
|
footerDiv.style.textAlign = "center";
|
|
|
|
// Create footer content
|
|
let linkSpan = document.createElement("a");
|
|
linkSpan.className = "small styled-link";
|
|
linkSpan.textContent = "accès au tableau de classement";
|
|
if (roundId) {
|
|
linkSpan.href = `/tournament/${tournamentId}/round/${roundId}/bracket/`;
|
|
linkSpan.style.cursor = "pointer";
|
|
}
|
|
|
|
footerDiv.appendChild(linkSpan);
|
|
|
|
// Create a container that will sit at the same position for all rounds
|
|
const footerContainer = document.createElement("div");
|
|
footerContainer.style.position = "absolute";
|
|
footerContainer.style.top = `${globalMaxBottom}px`; // Same position for all footers
|
|
footerContainer.style.width = "100%";
|
|
footerContainer.appendChild(footerDiv);
|
|
|
|
// Add to the round div
|
|
const matchesContainer =
|
|
roundDiv.querySelector(".matches-container");
|
|
matchesContainer.appendChild(footerContainer);
|
|
}
|
|
}
|
|
});
|
|
}, 100);
|
|
}
|
|
}
|
|
|